Re-add new, renamed account* files.
This commit is contained in:
parent
5590ee2258
commit
3b6a6cf6cc
3 changed files with 1011 additions and 0 deletions
836
evennia/commands/default/account.py
Normal file
836
evennia/commands/default/account.py
Normal file
|
|
@ -0,0 +1,836 @@
|
|||
"""
|
||||
Account (OOC) commands. These are stored on the Account object
|
||||
and self.caller is thus always an Account, not an Object/Character.
|
||||
|
||||
These commands go in the AccountCmdset and are accessible also
|
||||
when puppeting a Character (although with lower priority)
|
||||
|
||||
These commands use the account_caller property which tells the command
|
||||
parent (MuxCommand, usually) to setup caller correctly. They use
|
||||
self.account to make sure to always use the account object rather than
|
||||
self.caller (which change depending on the level you are calling from)
|
||||
The property self.character can be used to access the character when
|
||||
these commands are triggered with a connected character (such as the
|
||||
case of the @ooc command), it is None if we are OOC.
|
||||
|
||||
Note that under MULTISESSION_MODE > 2, Account commands should use
|
||||
self.msg() and similar methods to reroute returns to the correct
|
||||
method. Otherwise all text will be returned to all connected sessions.
|
||||
|
||||
"""
|
||||
from builtins import range
|
||||
|
||||
import time
|
||||
from django.conf import settings
|
||||
from evennia.server.sessionhandler import SESSIONS
|
||||
from evennia.utils import utils, create, search, evtable
|
||||
|
||||
COMMAND_DEFAULT_CLASS = utils.class_from_module(settings.COMMAND_DEFAULT_CLASS)
|
||||
|
||||
_MAX_NR_CHARACTERS = settings.MAX_NR_CHARACTERS
|
||||
_MULTISESSION_MODE = settings.MULTISESSION_MODE
|
||||
|
||||
# limit symbol import for API
|
||||
__all__ = ("CmdOOCLook", "CmdIC", "CmdOOC", "CmdPassword", "CmdQuit",
|
||||
"CmdCharCreate", "CmdOption", "CmdSessions", "CmdWho",
|
||||
"CmdColorTest", "CmdQuell")
|
||||
|
||||
|
||||
class MuxAccountLookCommand(COMMAND_DEFAULT_CLASS):
|
||||
"""
|
||||
Custom parent (only) parsing for OOC looking, sets a "playable"
|
||||
property on the command based on the parsing.
|
||||
|
||||
"""
|
||||
|
||||
def parse(self):
|
||||
"""Custom parsing"""
|
||||
|
||||
super(MuxAccountLookCommand, self).parse()
|
||||
|
||||
if _MULTISESSION_MODE < 2:
|
||||
# only one character allowed - not used in this mode
|
||||
self.playable = None
|
||||
return
|
||||
|
||||
playable = self.account.db._playable_characters
|
||||
if playable is not None:
|
||||
# clean up list if character object was deleted in between
|
||||
if None in playable:
|
||||
playable = [character for character in playable if character]
|
||||
self.account.db._playable_characters = playable
|
||||
# store playable property
|
||||
if self.args:
|
||||
self.playable = dict((utils.to_str(char.key.lower()), char)
|
||||
for char in playable).get(self.args.lower(), None)
|
||||
else:
|
||||
self.playable = playable
|
||||
|
||||
|
||||
# Obs - these are all intended to be stored on the Account, and as such,
|
||||
# use self.account instead of self.caller, just to be sure. Also self.msg()
|
||||
# is used to make sure returns go to the right session
|
||||
|
||||
# note that this is inheriting from MuxAccountLookCommand,
|
||||
# and has the .playable property.
|
||||
class CmdOOCLook(MuxAccountLookCommand):
|
||||
"""
|
||||
look while out-of-character
|
||||
|
||||
Usage:
|
||||
look
|
||||
|
||||
Look in the ooc state.
|
||||
"""
|
||||
|
||||
# This is an OOC version of the look command. Since a
|
||||
# Account doesn't have an in-game existence, there is no
|
||||
# concept of location or "self". If we are controlling
|
||||
# a character, pass control over to normal look.
|
||||
|
||||
key = "look"
|
||||
aliases = ["l", "ls"]
|
||||
locks = "cmd:all()"
|
||||
help_category = "General"
|
||||
|
||||
# this is used by the parent
|
||||
account_caller = True
|
||||
|
||||
def func(self):
|
||||
"""implement the ooc look command"""
|
||||
|
||||
if _MULTISESSION_MODE < 2:
|
||||
# only one character allowed
|
||||
self.msg("You are out-of-character (OOC).\nUse |w@ic|n to get back into the game.")
|
||||
return
|
||||
|
||||
# call on-account look helper method
|
||||
self.msg(self.account.at_look(target=self.playable, session=self.session))
|
||||
|
||||
|
||||
class CmdCharCreate(COMMAND_DEFAULT_CLASS):
|
||||
"""
|
||||
create a new character
|
||||
|
||||
Usage:
|
||||
@charcreate <charname> [= desc]
|
||||
|
||||
Create a new character, optionally giving it a description. You
|
||||
may use upper-case letters in the name - you will nevertheless
|
||||
always be able to access your character using lower-case letters
|
||||
if you want.
|
||||
"""
|
||||
key = "@charcreate"
|
||||
locks = "cmd:pperm(Account)"
|
||||
help_category = "General"
|
||||
|
||||
# this is used by the parent
|
||||
account_caller = True
|
||||
|
||||
def func(self):
|
||||
"""create the new character"""
|
||||
account = self.account
|
||||
if not self.args:
|
||||
self.msg("Usage: @charcreate <charname> [= description]")
|
||||
return
|
||||
key = self.lhs
|
||||
desc = self.rhs
|
||||
|
||||
charmax = _MAX_NR_CHARACTERS if _MULTISESSION_MODE > 1 else 1
|
||||
|
||||
if not account.is_superuser and \
|
||||
(account.db._playable_characters and
|
||||
len(account.db._playable_characters) >= charmax):
|
||||
self.msg("You may only create a maximum of %i characters." % charmax)
|
||||
return
|
||||
from evennia.objects.models import ObjectDB
|
||||
typeclass = settings.BASE_CHARACTER_TYPECLASS
|
||||
|
||||
if ObjectDB.objects.filter(db_typeclass_path=typeclass, db_key__iexact=key):
|
||||
# check if this Character already exists. Note that we are only
|
||||
# searching the base character typeclass here, not any child
|
||||
# classes.
|
||||
self.msg("|rA character named '|w%s|r' already exists.|n" % key)
|
||||
return
|
||||
|
||||
# create the character
|
||||
start_location = ObjectDB.objects.get_id(settings.START_LOCATION)
|
||||
default_home = ObjectDB.objects.get_id(settings.DEFAULT_HOME)
|
||||
permissions = settings.PERMISSION_ACCOUNT_DEFAULT
|
||||
new_character = create.create_object(typeclass, key=key,
|
||||
location=start_location,
|
||||
home=default_home,
|
||||
permissions=permissions)
|
||||
# only allow creator (and developers) to puppet this char
|
||||
new_character.locks.add("puppet:id(%i) or pid(%i) or perm(Developer) or pperm(Developer)" %
|
||||
(new_character.id, account.id))
|
||||
account.db._playable_characters.append(new_character)
|
||||
if desc:
|
||||
new_character.db.desc = desc
|
||||
elif not new_character.db.desc:
|
||||
new_character.db.desc = "This is an Account."
|
||||
self.msg("Created new character %s. Use |w@ic %s|n to enter the game as this character."
|
||||
% (new_character.key, new_character.key))
|
||||
|
||||
|
||||
class CmdCharDelete(COMMAND_DEFAULT_CLASS):
|
||||
"""
|
||||
delete a character - this cannot be undone!
|
||||
|
||||
Usage:
|
||||
@chardelete <charname>
|
||||
|
||||
Permanently deletes one of your characters.
|
||||
"""
|
||||
key = "@chardelete"
|
||||
locks = "cmd:pperm(Account)"
|
||||
help_category = "General"
|
||||
|
||||
def func(self):
|
||||
"""delete the character"""
|
||||
account = self.account
|
||||
|
||||
if not self.args:
|
||||
self.msg("Usage: @chardelete <charactername>")
|
||||
return
|
||||
|
||||
# use the playable_characters list to search
|
||||
match = [char for char in utils.make_iter(account.db._playable_characters)
|
||||
if char.key.lower() == self.args.lower()]
|
||||
if not match:
|
||||
self.msg("You have no such character to delete.")
|
||||
return
|
||||
elif len(match) > 1:
|
||||
self.msg("Aborting - there are two characters with the same name. Ask an admin to delete the right one.")
|
||||
return
|
||||
else: # one match
|
||||
from evennia.utils.evmenu import get_input
|
||||
|
||||
def _callback(caller, callback_prompt, result):
|
||||
if result.lower() == "yes":
|
||||
# only take action
|
||||
delobj = caller.ndb._char_to_delete
|
||||
key = delobj.key
|
||||
caller.db._playable_characters = [pc for pc in caller.db._playable_characters if pc != delobj]
|
||||
delobj.delete()
|
||||
self.msg("Character '%s' was permanently deleted." % key)
|
||||
else:
|
||||
self.msg("Deletion was aborted.")
|
||||
del caller.ndb._char_to_delete
|
||||
|
||||
match = match[0]
|
||||
account.ndb._char_to_delete = match
|
||||
prompt = "|rThis will permanently destroy '%s'. This cannot be undone.|n Continue yes/[no]?"
|
||||
get_input(account, prompt % match.key, _callback)
|
||||
|
||||
|
||||
class CmdIC(COMMAND_DEFAULT_CLASS):
|
||||
"""
|
||||
control an object you have permission to puppet
|
||||
|
||||
Usage:
|
||||
@ic <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. Note that it's the ACCOUNT character that puppets
|
||||
characters/objects and which needs to have the correct permission!
|
||||
|
||||
You cannot become an object that is already controlled by another
|
||||
account. In principle <character> can be any in-game object as long
|
||||
as you the account have access right to puppet it.
|
||||
"""
|
||||
|
||||
key = "@ic"
|
||||
# lock must be all() for different puppeted objects to access it.
|
||||
locks = "cmd:all()"
|
||||
aliases = "@puppet"
|
||||
help_category = "General"
|
||||
|
||||
# this is used by the parent
|
||||
account_caller = True
|
||||
|
||||
def func(self):
|
||||
"""
|
||||
Main puppet method
|
||||
"""
|
||||
account = self.account
|
||||
session = self.session
|
||||
|
||||
new_character = None
|
||||
if not self.args:
|
||||
new_character = account.db._last_puppet
|
||||
if not new_character:
|
||||
self.msg("Usage: @ic <character>")
|
||||
return
|
||||
if not new_character:
|
||||
# search for a matching character
|
||||
new_character = [char for char in search.object_search(self.args) if char.access(account, "puppet")]
|
||||
if not new_character:
|
||||
self.msg("That is not a valid character choice.")
|
||||
return
|
||||
if len(new_character) > 1:
|
||||
self.msg("Multiple targets with the same name:\n %s"
|
||||
% ", ".join("%s(#%s)" % (obj.key, obj.id) for obj in new_character))
|
||||
return
|
||||
else:
|
||||
new_character = new_character[0]
|
||||
try:
|
||||
account.puppet_object(session, new_character)
|
||||
account.db._last_puppet = new_character
|
||||
except RuntimeError as exc:
|
||||
self.msg("|rYou cannot become |C%s|n: %s" % (new_character.name, exc))
|
||||
|
||||
|
||||
# note that this is inheriting from MuxAccountLookCommand,
|
||||
# and as such has the .playable property.
|
||||
class CmdOOC(MuxAccountLookCommand):
|
||||
"""
|
||||
stop puppeting and go ooc
|
||||
|
||||
Usage:
|
||||
@ooc
|
||||
|
||||
Go out-of-character (OOC).
|
||||
|
||||
This will leave your current character and put you in a incorporeal OOC state.
|
||||
"""
|
||||
|
||||
key = "@ooc"
|
||||
locks = "cmd:pperm(Account)"
|
||||
aliases = "@unpuppet"
|
||||
help_category = "General"
|
||||
|
||||
# this is used by the parent
|
||||
account_caller = True
|
||||
|
||||
def func(self):
|
||||
"""Implement function"""
|
||||
|
||||
account = self.account
|
||||
session = self.session
|
||||
|
||||
old_char = account.get_puppet(session)
|
||||
if not old_char:
|
||||
string = "You are already OOC."
|
||||
self.msg(string)
|
||||
return
|
||||
|
||||
account.db._last_puppet = old_char
|
||||
|
||||
# disconnect
|
||||
try:
|
||||
account.unpuppet_object(session)
|
||||
self.msg("\n|GYou go OOC.|n\n")
|
||||
|
||||
if _MULTISESSION_MODE < 2:
|
||||
# only one character allowed
|
||||
self.msg("You are out-of-character (OOC).\nUse |w@ic|n to get back into the game.")
|
||||
return
|
||||
|
||||
self.msg(account.at_look(target=self.playable, session=session))
|
||||
|
||||
except RuntimeError as exc:
|
||||
self.msg("|rCould not unpuppet from |c%s|n: %s" % (old_char, exc))
|
||||
|
||||
|
||||
class CmdSessions(COMMAND_DEFAULT_CLASS):
|
||||
"""
|
||||
check your connected session(s)
|
||||
|
||||
Usage:
|
||||
@sessions
|
||||
|
||||
Lists the sessions currently connected to your account.
|
||||
|
||||
"""
|
||||
key = "@sessions"
|
||||
locks = "cmd:all()"
|
||||
help_category = "General"
|
||||
|
||||
# this is used by the parent
|
||||
account_caller = True
|
||||
|
||||
def func(self):
|
||||
"""Implement function"""
|
||||
account = self.account
|
||||
sessions = account.sessions.all()
|
||||
table = evtable.EvTable("|wsessid",
|
||||
"|wprotocol",
|
||||
"|whost",
|
||||
"|wpuppet/character",
|
||||
"|wlocation")
|
||||
for sess in sorted(sessions, key=lambda x: x.sessid):
|
||||
char = account.get_puppet(sess)
|
||||
table.add_row(str(sess.sessid), str(sess.protocol_key),
|
||||
type(sess.address) == tuple and sess.address[0] or sess.address,
|
||||
char and str(char) or "None",
|
||||
char and str(char.location) or "N/A")
|
||||
self.msg("|wYour current session(s):|n\n%s" % table)
|
||||
|
||||
|
||||
class CmdWho(COMMAND_DEFAULT_CLASS):
|
||||
"""
|
||||
list who is currently online
|
||||
|
||||
Usage:
|
||||
who
|
||||
doing
|
||||
|
||||
Shows who is currently online. Doing is an alias that limits info
|
||||
also for those with all permissions.
|
||||
"""
|
||||
|
||||
key = "who"
|
||||
aliases = "doing"
|
||||
locks = "cmd:all()"
|
||||
|
||||
# this is used by the parent
|
||||
account_caller = True
|
||||
|
||||
def func(self):
|
||||
"""
|
||||
Get all connected accounts by polling session.
|
||||
"""
|
||||
|
||||
account = self.account
|
||||
session_list = SESSIONS.get_sessions()
|
||||
|
||||
session_list = sorted(session_list, key=lambda o: o.account.key)
|
||||
|
||||
if self.cmdstring == "doing":
|
||||
show_session_data = False
|
||||
else:
|
||||
show_session_data = account.check_permstring("Developer") or account.check_permstring("Admins")
|
||||
|
||||
naccounts = (SESSIONS.account_count())
|
||||
if show_session_data:
|
||||
# privileged info
|
||||
table = evtable.EvTable("|wAccount Name",
|
||||
"|wOn for",
|
||||
"|wIdle",
|
||||
"|wPuppeting",
|
||||
"|wRoom",
|
||||
"|wCmds",
|
||||
"|wProtocol",
|
||||
"|wHost")
|
||||
for session in session_list:
|
||||
if not session.logged_in:
|
||||
continue
|
||||
delta_cmd = time.time() - session.cmd_last_visible
|
||||
delta_conn = time.time() - session.conn_time
|
||||
account = session.get_account()
|
||||
puppet = session.get_puppet()
|
||||
location = puppet.location.key if puppet and puppet.location else "None"
|
||||
table.add_row(utils.crop(account.name, width=25),
|
||||
utils.time_format(delta_conn, 0),
|
||||
utils.time_format(delta_cmd, 1),
|
||||
utils.crop(puppet.key if puppet else "None", width=25),
|
||||
utils.crop(location, width=25),
|
||||
session.cmd_total,
|
||||
session.protocol_key,
|
||||
isinstance(session.address, tuple) and session.address[0] or session.address)
|
||||
else:
|
||||
# unprivileged
|
||||
table = evtable.EvTable("|wAccount name", "|wOn for", "|wIdle")
|
||||
for session in session_list:
|
||||
if not session.logged_in:
|
||||
continue
|
||||
delta_cmd = time.time() - session.cmd_last_visible
|
||||
delta_conn = time.time() - session.conn_time
|
||||
account = session.get_account()
|
||||
table.add_row(utils.crop(account.key, width=25),
|
||||
utils.time_format(delta_conn, 0),
|
||||
utils.time_format(delta_cmd, 1))
|
||||
is_one = naccounts == 1
|
||||
self.msg("|wAccounts:|n\n%s\n%s unique account%s logged in."
|
||||
% (table, "One" if is_one else naccounts, "" if is_one else "s"))
|
||||
|
||||
|
||||
class CmdOption(COMMAND_DEFAULT_CLASS):
|
||||
"""
|
||||
Set an account option
|
||||
|
||||
Usage:
|
||||
@option[/save] [name = value]
|
||||
|
||||
Switch:
|
||||
save - Save the current option settings for future logins.
|
||||
clear - Clear the saved options.
|
||||
|
||||
This command allows for viewing and setting client interface
|
||||
settings. Note that saved options may not be able to be used if
|
||||
later connecting with a client with different capabilities.
|
||||
|
||||
|
||||
"""
|
||||
key = "@option"
|
||||
aliases = "@options"
|
||||
locks = "cmd:all()"
|
||||
|
||||
# this is used by the parent
|
||||
account_caller = True
|
||||
|
||||
def func(self):
|
||||
"""
|
||||
Implements the command
|
||||
"""
|
||||
if self.session is None:
|
||||
return
|
||||
|
||||
flags = self.session.protocol_flags
|
||||
|
||||
# Display current options
|
||||
if not self.args:
|
||||
# list the option settings
|
||||
|
||||
if "save" in self.switches:
|
||||
# save all options
|
||||
self.caller.db._saved_protocol_flags = flags
|
||||
self.msg("|gSaved all options. Use @option/clear to remove.|n")
|
||||
if "clear" in self.switches:
|
||||
# clear all saves
|
||||
self.caller.db._saved_protocol_flags = {}
|
||||
self.msg("|gCleared all saved options.")
|
||||
|
||||
options = dict(flags) # make a copy of the flag dict
|
||||
saved_options = dict(self.caller.attributes.get("_saved_protocol_flags", default={}))
|
||||
|
||||
if "SCREENWIDTH" in options:
|
||||
if len(options["SCREENWIDTH"]) == 1:
|
||||
options["SCREENWIDTH"] = options["SCREENWIDTH"][0]
|
||||
else:
|
||||
options["SCREENWIDTH"] = " \n".join("%s : %s" % (screenid, size)
|
||||
for screenid, size in options["SCREENWIDTH"].iteritems())
|
||||
if "SCREENHEIGHT" in options:
|
||||
if len(options["SCREENHEIGHT"]) == 1:
|
||||
options["SCREENHEIGHT"] = options["SCREENHEIGHT"][0]
|
||||
else:
|
||||
options["SCREENHEIGHT"] = " \n".join("%s : %s" % (screenid, size)
|
||||
for screenid, size in options["SCREENHEIGHT"].iteritems())
|
||||
options.pop("TTYPE", None)
|
||||
|
||||
header = ("Name", "Value", "Saved") if saved_options else ("Name", "Value")
|
||||
table = evtable.EvTable(*header)
|
||||
for key in sorted(options):
|
||||
row = [key, options[key]]
|
||||
if saved_options:
|
||||
saved = " |YYes|n" if key in saved_options else ""
|
||||
changed = "|y*|n" if key in saved_options and flags[key] != saved_options[key] else ""
|
||||
row.append("%s%s" % (saved, changed))
|
||||
table.add_row(*row)
|
||||
self.msg("|wClient settings (%s):|n\n%s|n" % (self.session.protocol_key, table))
|
||||
|
||||
return
|
||||
|
||||
if not self.rhs:
|
||||
self.msg("Usage: @option [name = [value]]")
|
||||
return
|
||||
|
||||
# Try to assign new values
|
||||
|
||||
def validate_encoding(new_encoding):
|
||||
# helper: change encoding
|
||||
try:
|
||||
utils.to_str(utils.to_unicode("test-string"), encoding=new_encoding)
|
||||
except LookupError:
|
||||
raise RuntimeError("The encoding '|w%s|n' is invalid. " % new_encoding)
|
||||
return val
|
||||
|
||||
def validate_size(new_size):
|
||||
return {0: int(new_size)}
|
||||
|
||||
def validate_bool(new_bool):
|
||||
return True if new_bool.lower() in ("true", "on", "1") else False
|
||||
|
||||
def update(new_name, new_val, validator):
|
||||
# helper: update property and report errors
|
||||
try:
|
||||
old_val = flags.get(new_name, False)
|
||||
new_val = validator(new_val)
|
||||
flags[new_name] = new_val
|
||||
self.msg("Option |w%s|n was changed from '|w%s|n' to '|w%s|n'." % (new_name, old_val, new_val))
|
||||
return {new_name: new_val}
|
||||
except Exception, err:
|
||||
self.msg("|rCould not set option |w%s|r:|n %s" % (new_name, err))
|
||||
return False
|
||||
|
||||
validators = {"ANSI": validate_bool,
|
||||
"CLIENTNAME": utils.to_str,
|
||||
"ENCODING": validate_encoding,
|
||||
"MCCP": validate_bool,
|
||||
"NOGOAHEAD": validate_bool,
|
||||
"MXP": validate_bool,
|
||||
"NOCOLOR": validate_bool,
|
||||
"NOPKEEPALIVE": validate_bool,
|
||||
"OOB": validate_bool,
|
||||
"RAW": validate_bool,
|
||||
"SCREENHEIGHT": validate_size,
|
||||
"SCREENWIDTH": validate_size,
|
||||
"SCREENREADER": validate_bool,
|
||||
"TERM": utils.to_str,
|
||||
"UTF-8": validate_bool,
|
||||
"XTERM256": validate_bool,
|
||||
"INPUTDEBUG": validate_bool}
|
||||
|
||||
name = self.lhs.upper()
|
||||
val = self.rhs.strip()
|
||||
optiondict = False
|
||||
if val and name in validators:
|
||||
optiondict = update(name, val, validators[name])
|
||||
else:
|
||||
self.msg("|rNo option named '|w%s|r'." % name)
|
||||
if optiondict:
|
||||
# a valid setting
|
||||
if "save" in self.switches:
|
||||
# save this option only
|
||||
saved_options = self.account.attributes.get("_saved_protocol_flags", default={})
|
||||
saved_options.update(optiondict)
|
||||
self.account.attributes.add("_saved_protocol_flags", saved_options)
|
||||
for key in optiondict:
|
||||
self.msg("|gSaved option %s.|n" % key)
|
||||
if "clear" in self.switches:
|
||||
# clear this save
|
||||
for key in optiondict:
|
||||
self.account.attributes.get("_saved_protocol_flags", {}).pop(key, None)
|
||||
self.msg("|gCleared saved %s." % key)
|
||||
self.session.update_flags(**optiondict)
|
||||
|
||||
|
||||
class CmdPassword(COMMAND_DEFAULT_CLASS):
|
||||
"""
|
||||
change your password
|
||||
|
||||
Usage:
|
||||
@password <old password> = <new password>
|
||||
|
||||
Changes your password. Make sure to pick a safe one.
|
||||
"""
|
||||
key = "@password"
|
||||
locks = "cmd:pperm(Account)"
|
||||
|
||||
# this is used by the parent
|
||||
account_caller = True
|
||||
|
||||
def func(self):
|
||||
"""hook function."""
|
||||
|
||||
account = self.account
|
||||
if not self.rhs:
|
||||
self.msg("Usage: @password <oldpass> = <newpass>")
|
||||
return
|
||||
oldpass = self.lhslist[0] # Both of these are
|
||||
newpass = self.rhslist[0] # already stripped by parse()
|
||||
if not account.check_password(oldpass):
|
||||
self.msg("The specified old password isn't correct.")
|
||||
elif len(newpass) < 3:
|
||||
self.msg("Passwords must be at least three characters long.")
|
||||
else:
|
||||
account.set_password(newpass)
|
||||
account.save()
|
||||
self.msg("Password changed.")
|
||||
|
||||
|
||||
class CmdQuit(COMMAND_DEFAULT_CLASS):
|
||||
"""
|
||||
quit the game
|
||||
|
||||
Usage:
|
||||
@quit
|
||||
|
||||
Switch:
|
||||
all - disconnect all connected sessions
|
||||
|
||||
Gracefully disconnect your current session from the
|
||||
game. Use the /all switch to disconnect from all sessions.
|
||||
"""
|
||||
key = "@quit"
|
||||
locks = "cmd:all()"
|
||||
|
||||
# this is used by the parent
|
||||
account_caller = True
|
||||
|
||||
def func(self):
|
||||
"""hook function"""
|
||||
account = self.account
|
||||
|
||||
if 'all' in self.switches:
|
||||
account.msg("|RQuitting|n all sessions. Hope to see you soon again.", session=self.session)
|
||||
for session in account.sessions.all():
|
||||
account.disconnect_session_from_account(session)
|
||||
else:
|
||||
nsess = len(account.sessions.all())
|
||||
if nsess == 2:
|
||||
account.msg("|RQuitting|n. One session is still connected.", session=self.session)
|
||||
elif nsess > 2:
|
||||
account.msg("|RQuitting|n. %i sessions are still connected." % (nsess-1), session=self.session)
|
||||
else:
|
||||
# we are quitting the last available session
|
||||
account.msg("|RQuitting|n. Hope to see you again, soon.", session=self.session)
|
||||
account.disconnect_session_from_account(self.session)
|
||||
|
||||
|
||||
class CmdColorTest(COMMAND_DEFAULT_CLASS):
|
||||
"""
|
||||
testing which colors your client support
|
||||
|
||||
Usage:
|
||||
@color ansi||xterm256
|
||||
|
||||
Prints a color map along with in-mud color codes to use to produce
|
||||
them. It also tests what is supported in your client. Choices are
|
||||
16-color ansi (supported in most muds) or the 256-color xterm256
|
||||
standard. No checking is done to determine your client supports
|
||||
color - if not you will see rubbish appear.
|
||||
"""
|
||||
key = "@color"
|
||||
locks = "cmd:all()"
|
||||
help_category = "General"
|
||||
|
||||
# this is used by the parent
|
||||
account_caller = True
|
||||
|
||||
def table_format(self, table):
|
||||
"""
|
||||
Helper method to format the ansi/xterm256 tables.
|
||||
Takes a table of columns [[val,val,...],[val,val,...],...]
|
||||
"""
|
||||
if not table:
|
||||
return [[]]
|
||||
|
||||
extra_space = 1
|
||||
max_widths = [max([len(str(val)) for val in col]) for col in table]
|
||||
ftable = []
|
||||
for irow in range(len(table[0])):
|
||||
ftable.append([str(col[irow]).ljust(max_widths[icol]) + " " *
|
||||
extra_space for icol, col in enumerate(table)])
|
||||
return ftable
|
||||
|
||||
def func(self):
|
||||
"""Show color tables"""
|
||||
|
||||
if self.args.startswith("a"):
|
||||
# show ansi 16-color table
|
||||
from evennia.utils import ansi
|
||||
ap = ansi.ANSI_PARSER
|
||||
# ansi colors
|
||||
# show all ansi color-related codes
|
||||
col1 = ["%s%s|n" % (code, code.replace("|", "||")) for code, _ in ap.ext_ansi_map[48:56]]
|
||||
col2 = ["%s%s|n" % (code, code.replace("|", "||")) for code, _ in ap.ext_ansi_map[56:64]]
|
||||
col3 = ["%s%s|n" % (code.replace("\\", ""), code.replace("|", "||").replace("\\", ""))
|
||||
for code, _ in ap.ext_ansi_map[-8:]]
|
||||
col4 = ["%s%s|n" % (code.replace("\\", ""), code.replace("|", "||").replace("\\", ""))
|
||||
for code, _ in ap.ansi_bright_bgs[-8:]]
|
||||
col2.extend(["" for _ in range(len(col1)-len(col2))])
|
||||
table = utils.format_table([col1, col2, col4, col3])
|
||||
string = "ANSI colors:"
|
||||
for row in table:
|
||||
string += "\n " + " ".join(row)
|
||||
self.msg(string)
|
||||
self.msg("||X : black. ||/ : return, ||- : tab, ||_ : space, ||* : invert, ||u : underline\n"
|
||||
"To combine background and foreground, add background marker last, e.g. ||r||[B.\n"
|
||||
"Note: bright backgrounds like ||[r requires your client handling Xterm256 colors.")
|
||||
|
||||
elif self.args.startswith("x"):
|
||||
# show xterm256 table
|
||||
table = [[], [], [], [], [], [], [], [], [], [], [], []]
|
||||
for ir in range(6):
|
||||
for ig in range(6):
|
||||
for ib in range(6):
|
||||
# foreground table
|
||||
table[ir].append("|%i%i%i%s|n" % (ir, ig, ib, "||%i%i%i" % (ir, ig, ib)))
|
||||
# background table
|
||||
table[6+ir].append("|%i%i%i|[%i%i%i%s|n"
|
||||
% (5 - ir, 5 - ig, 5 - ib, ir, ig, ib, "||[%i%i%i" % (ir, ig, ib)))
|
||||
table = self.table_format(table)
|
||||
string = "Xterm256 colors (if not all hues show, your client might not report that it can handle xterm256):"
|
||||
string += "\n" + "\n".join("".join(row) for row in table)
|
||||
table = [[], [], [], [], [], [], [], [], [], [], [], []]
|
||||
for ibatch in range(4):
|
||||
for igray in range(6):
|
||||
letter = chr(97 + (ibatch*6 + igray))
|
||||
inverse = chr(122 - (ibatch*6 + igray))
|
||||
table[0 + igray].append("|=%s%s |n" % (letter, "||=%s" % letter))
|
||||
table[6 + igray].append("|=%s|[=%s%s |n" % (inverse, letter, "||[=%s" % letter))
|
||||
for igray in range(6):
|
||||
# the last row (y, z) has empty columns
|
||||
if igray < 2:
|
||||
letter = chr(121 + igray)
|
||||
inverse = chr(98 - igray)
|
||||
fg = "|=%s%s |n" % (letter, "||=%s" % letter)
|
||||
bg = "|=%s|[=%s%s |n" % (inverse, letter, "||[=%s" % letter)
|
||||
else:
|
||||
fg, bg = " ", " "
|
||||
table[0 + igray].append(fg)
|
||||
table[6 + igray].append(bg)
|
||||
table = self.table_format(table)
|
||||
string += "\n" + "\n".join("".join(row) for row in table)
|
||||
self.msg(string)
|
||||
else:
|
||||
# malformed input
|
||||
self.msg("Usage: @color ansi||xterm256")
|
||||
|
||||
|
||||
class CmdQuell(COMMAND_DEFAULT_CLASS):
|
||||
"""
|
||||
use character's permissions instead of account's
|
||||
|
||||
Usage:
|
||||
quell
|
||||
unquell
|
||||
|
||||
Normally the permission level of the Account is used when puppeting a
|
||||
Character/Object to determine access. This command will switch the lock
|
||||
system to make use of the puppeted Object's permissions instead. This is
|
||||
useful mainly for testing.
|
||||
Hierarchical permission quelling only work downwards, thus an Account cannot
|
||||
use a higher-permission Character to escalate their permission level.
|
||||
Use the unquell command to revert back to normal operation.
|
||||
"""
|
||||
|
||||
key = "@quell"
|
||||
aliases = ["@unquell"]
|
||||
locks = "cmd:pperm(Account)"
|
||||
help_category = "General"
|
||||
|
||||
# this is used by the parent
|
||||
account_caller = True
|
||||
|
||||
def _recache_locks(self, account):
|
||||
"""Helper method to reset the lockhandler on an already puppeted object"""
|
||||
if self.session:
|
||||
char = self.session.puppet
|
||||
if char:
|
||||
# we are already puppeting an object. We need to reset
|
||||
# the lock caches (otherwise the superuser status change
|
||||
# won't be visible until repuppet)
|
||||
char.locks.reset()
|
||||
account.locks.reset()
|
||||
|
||||
def func(self):
|
||||
"""Perform the command"""
|
||||
account = self.account
|
||||
permstr = account.is_superuser and " (superuser)" or "(%s)" % (", ".join(account.permissions.all()))
|
||||
if self.cmdstring == '@unquell':
|
||||
if not account.attributes.get('_quell'):
|
||||
self.msg("Already using normal Account permissions %s." % permstr)
|
||||
else:
|
||||
account.attributes.remove('_quell')
|
||||
self.msg("Account permissions %s restored." % permstr)
|
||||
else:
|
||||
if account.attributes.get('_quell'):
|
||||
self.msg("Already quelling Account %s permissions." % permstr)
|
||||
return
|
||||
account.attributes.add('_quell', True)
|
||||
puppet = self.session.puppet
|
||||
if puppet:
|
||||
cpermstr = "(%s)" % ", ".join(puppet.permissions.all())
|
||||
cpermstr = "Quelling to current puppet's permissions %s." % cpermstr
|
||||
cpermstr += "\n(Note: If this is higher than Account permissions %s," \
|
||||
" the lowest of the two will be used.)" % permstr
|
||||
cpermstr += "\nUse @unquell to return to normal permission usage."
|
||||
self.msg(cpermstr)
|
||||
else:
|
||||
self.msg("Quelling Account permissions%s. Use @unquell to get them back." % permstr)
|
||||
self._recache_locks(account)
|
||||
73
evennia/commands/default/cmdset_account.py
Normal file
73
evennia/commands/default/cmdset_account.py
Normal file
|
|
@ -0,0 +1,73 @@
|
|||
"""
|
||||
|
||||
This is the cmdset for Account (OOC) commands. These are
|
||||
stored on the Account object and should thus be able to handle getting
|
||||
an Account object as caller rather than a Character.
|
||||
|
||||
Note - in order for session-rerouting (in MULTISESSION_MODE=2) to
|
||||
function, all commands in this cmdset should use the self.msg()
|
||||
command method rather than caller.msg().
|
||||
"""
|
||||
|
||||
from evennia.commands.cmdset import CmdSet
|
||||
from evennia.commands.default import help, comms, admin, system
|
||||
from evennia.commands.default import building, account
|
||||
|
||||
|
||||
class AccountCmdSet(CmdSet):
|
||||
"""
|
||||
Implements the account command set.
|
||||
"""
|
||||
|
||||
key = "DefaultAccount"
|
||||
priority = -10
|
||||
|
||||
def at_cmdset_creation(self):
|
||||
"Populates the cmdset"
|
||||
|
||||
# Account-specific commands
|
||||
self.add(account.CmdOOCLook())
|
||||
self.add(account.CmdIC())
|
||||
self.add(account.CmdOOC())
|
||||
self.add(account.CmdCharCreate())
|
||||
self.add(account.CmdCharDelete())
|
||||
#self.add(account.CmdSessions())
|
||||
self.add(account.CmdWho())
|
||||
self.add(account.CmdOption())
|
||||
self.add(account.CmdQuit())
|
||||
self.add(account.CmdPassword())
|
||||
self.add(account.CmdColorTest())
|
||||
self.add(account.CmdQuell())
|
||||
|
||||
# testing
|
||||
self.add(building.CmdExamine())
|
||||
|
||||
# Help command
|
||||
self.add(help.CmdHelp())
|
||||
|
||||
# system commands
|
||||
self.add(system.CmdReload())
|
||||
self.add(system.CmdReset())
|
||||
self.add(system.CmdShutdown())
|
||||
self.add(system.CmdPy())
|
||||
|
||||
# Admin commands
|
||||
self.add(admin.CmdDelAccount())
|
||||
self.add(admin.CmdNewPassword())
|
||||
|
||||
# Comm commands
|
||||
self.add(comms.CmdAddCom())
|
||||
self.add(comms.CmdDelCom())
|
||||
self.add(comms.CmdAllCom())
|
||||
self.add(comms.CmdChannels())
|
||||
self.add(comms.CmdCdestroy())
|
||||
self.add(comms.CmdChannelCreate())
|
||||
self.add(comms.CmdClock())
|
||||
self.add(comms.CmdCBoot())
|
||||
self.add(comms.CmdCemit())
|
||||
self.add(comms.CmdCWho())
|
||||
self.add(comms.CmdCdesc())
|
||||
self.add(comms.CmdPage())
|
||||
self.add(comms.CmdIRC2Chan())
|
||||
self.add(comms.CmdIRCStatus())
|
||||
self.add(comms.CmdRSS2Chan())
|
||||
102
evennia/game_template/typeclasses/accounts.py
Normal file
102
evennia/game_template/typeclasses/accounts.py
Normal file
|
|
@ -0,0 +1,102 @@
|
|||
"""
|
||||
Account
|
||||
|
||||
The Account represents the game "account" and each login has only one
|
||||
Account object. An Account is what chats on default channels but has no
|
||||
other in-game-world existence. Rather the Account puppets Objects (such
|
||||
as Characters) in order to actually participate in the game world.
|
||||
|
||||
|
||||
Guest
|
||||
|
||||
Guest accounts are simple low-level accounts that are created/deleted
|
||||
on the fly and allows users to test the game without the commitment
|
||||
of a full registration. Guest accounts are deactivated by default; to
|
||||
activate them, add the following line to your settings file:
|
||||
|
||||
GUEST_ENABLED = True
|
||||
|
||||
You will also need to modify the connection screen to reflect the
|
||||
possibility to connect with a guest account. The setting file accepts
|
||||
several more options for customizing the Guest account system.
|
||||
|
||||
"""
|
||||
|
||||
from evennia import DefaultAccount, DefaultGuest
|
||||
|
||||
class Account(DefaultAccount):
|
||||
"""
|
||||
This class describes the actual OOC account (i.e. the user connecting
|
||||
to the MUD). It does NOT have visual appearance in the game world (that
|
||||
is handled by the character which is connected to this). Comm channels
|
||||
are attended/joined using this object.
|
||||
|
||||
It can be useful e.g. for storing configuration options for your game, but
|
||||
should generally not hold any character-related info (that's best handled
|
||||
on the character level).
|
||||
|
||||
Can be set using BASE_ACCOUNT_TYPECLASS.
|
||||
|
||||
|
||||
* available properties
|
||||
|
||||
key (string) - name of account
|
||||
name (string)- wrapper for user.username
|
||||
aliases (list of strings) - aliases to the object. Will be saved to database as AliasDB entries but returned as strings.
|
||||
dbref (int, read-only) - unique #id-number. Also "id" can be used.
|
||||
date_created (string) - time stamp of object creation
|
||||
permissions (list of strings) - list of permission strings
|
||||
|
||||
user (User, read-only) - django User authorization object
|
||||
obj (Object) - game object controlled by account. 'character' can also be used.
|
||||
sessions (list of Sessions) - sessions connected to this account
|
||||
is_superuser (bool, read-only) - if the connected user is a superuser
|
||||
|
||||
* Handlers
|
||||
|
||||
locks - lock-handler: use locks.add() to add new lock strings
|
||||
db - attribute-handler: store/retrieve database attributes on this self.db.myattr=val, val=self.db.myattr
|
||||
ndb - non-persistent attribute handler: same as db but does not create a database entry when storing data
|
||||
scripts - script-handler. Add new scripts to object with scripts.add()
|
||||
cmdset - cmdset-handler. Use cmdset.add() to add new cmdsets to object
|
||||
nicks - nick-handler. New nicks with nicks.add().
|
||||
|
||||
* Helper methods
|
||||
|
||||
msg(text=None, **kwargs)
|
||||
swap_character(new_character, delete_old_character=False)
|
||||
execute_cmd(raw_string, session=None)
|
||||
search(ostring, global_search=False, attribute_name=None, use_nicks=False, location=None, ignore_errors=False, account=False)
|
||||
is_typeclass(typeclass, exact=False)
|
||||
swap_typeclass(new_typeclass, clean_attributes=False, no_default=True)
|
||||
access(accessing_obj, access_type='read', default=False)
|
||||
check_permstring(permstring)
|
||||
|
||||
* Hook methods (when re-implementation, remember methods need to have self as first arg)
|
||||
|
||||
basetype_setup()
|
||||
at_account_creation()
|
||||
|
||||
- note that the following hooks are also found on Objects and are
|
||||
usually handled on the character level:
|
||||
|
||||
at_init()
|
||||
at_cmdset_get(**kwargs)
|
||||
at_first_login()
|
||||
at_post_login(session=None)
|
||||
at_disconnect()
|
||||
at_message_receive()
|
||||
at_message_send()
|
||||
at_server_reload()
|
||||
at_server_shutdown()
|
||||
|
||||
"""
|
||||
pass
|
||||
|
||||
|
||||
class Guest(DefaultGuest):
|
||||
"""
|
||||
This class is used for guest logins. Unlike Accounts, Guests and their
|
||||
characters are deleted after disconnection.
|
||||
"""
|
||||
pass
|
||||
Loading…
Add table
Add a link
Reference in a new issue