Fix merge conflicts
This commit is contained in:
commit
981119b640
89 changed files with 5435 additions and 818 deletions
|
|
@ -163,8 +163,8 @@ class CmdCharCreate(COMMAND_DEFAULT_CLASS):
|
|||
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))
|
||||
new_character.locks.add("puppet:id(%i) or pid(%i) or perm(Developer) or pperm(Developer);delete:id(%i) or perm(Admin)" %
|
||||
(new_character.id, account.id, account.id))
|
||||
account.db._playable_characters.append(new_character)
|
||||
if desc:
|
||||
new_character.db.desc = desc
|
||||
|
|
@ -223,6 +223,12 @@ class CmdCharDelete(COMMAND_DEFAULT_CLASS):
|
|||
|
||||
match = match[0]
|
||||
account.ndb._char_to_delete = match
|
||||
|
||||
# Return if caller has no permission to delete this
|
||||
if not match.access(account, 'delete'):
|
||||
self.msg("You do not have permission to delete this character.")
|
||||
return
|
||||
|
||||
prompt = "|rThis will permanently destroy '%s'. This cannot be undone.|n Continue yes/[no]?"
|
||||
get_input(account, prompt % match.key, _callback)
|
||||
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ PERMISSION_HIERARCHY = [p.lower() for p in settings.PERMISSION_HIERARCHY]
|
|||
|
||||
# limit members for API inclusion
|
||||
__all__ = ("CmdBoot", "CmdBan", "CmdUnban",
|
||||
"CmdEmit", "CmdNewPassword", "CmdPerm", "CmdWall")
|
||||
"CmdEmit", "CmdNewPassword", "CmdPerm", "CmdWall", "CmdForce")
|
||||
|
||||
|
||||
class CmdBoot(COMMAND_DEFAULT_CLASS):
|
||||
|
|
@ -513,3 +513,33 @@ class CmdWall(COMMAND_DEFAULT_CLASS):
|
|||
message = "%s shouts \"%s\"" % (self.caller.name, self.args)
|
||||
self.msg("Announcing to all connected sessions ...")
|
||||
SESSIONS.announce_all(message)
|
||||
|
||||
|
||||
class CmdForce(COMMAND_DEFAULT_CLASS):
|
||||
"""
|
||||
forces an object to execute a command
|
||||
|
||||
Usage:
|
||||
@force <object>=<command string>
|
||||
|
||||
Example:
|
||||
@force bob=get stick
|
||||
"""
|
||||
key = "@force"
|
||||
locks = "cmd:perm(spawn) or perm(Builder)"
|
||||
help_category = "Building"
|
||||
perm_used = "edit"
|
||||
|
||||
def func(self):
|
||||
"""Implements the force command"""
|
||||
if not self.lhs or not self.rhs:
|
||||
self.caller.msg("You must provide a target and a command string to execute.")
|
||||
return
|
||||
targ = self.caller.search(self.lhs)
|
||||
if not targ:
|
||||
return
|
||||
if not targ.access(self.caller, self.perm_used):
|
||||
self.caller.msg("You don't have permission to force them to execute commands.")
|
||||
return
|
||||
targ.execute_cmd(self.rhs)
|
||||
self.caller.msg("You have forced %s to: %s" % (targ, self.rhs))
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ from evennia.objects.models import ObjectDB
|
|||
from evennia.locks.lockhandler import LockException
|
||||
from evennia.commands.cmdhandler import get_and_merge_cmdsets
|
||||
from evennia.utils import create, utils, search
|
||||
from evennia.utils.utils import inherits_from, class_from_module, get_all_typeclasses
|
||||
from evennia.utils.utils import inherits_from, class_from_module, get_all_typeclasses, variable_from_module
|
||||
from evennia.utils.eveditor import EvEditor
|
||||
from evennia.utils.evmore import EvMore
|
||||
from evennia.prototypes import spawner, prototypes as protlib, menus as olc_menus
|
||||
|
|
@ -612,12 +612,12 @@ class CmdDesc(COMMAND_DEFAULT_CLASS):
|
|||
self.edit_handler()
|
||||
return
|
||||
|
||||
if self.rhs:
|
||||
if '=' in self.args:
|
||||
# We have an =
|
||||
obj = caller.search(self.lhs)
|
||||
if not obj:
|
||||
return
|
||||
desc = self.rhs
|
||||
desc = self.rhs or ''
|
||||
else:
|
||||
obj = caller.location or self.msg("|rYou can't describe oblivion.|n")
|
||||
if not obj:
|
||||
|
|
@ -1022,10 +1022,17 @@ class CmdLink(COMMAND_DEFAULT_CLASS):
|
|||
|
||||
object_name = self.lhs
|
||||
|
||||
# get object
|
||||
obj = caller.search(object_name, global_search=True)
|
||||
if not obj:
|
||||
return
|
||||
# try to search locally first
|
||||
results = caller.search(object_name, quiet=True)
|
||||
if len(results) > 1: # local results was a multimatch. Inform them to be more specific
|
||||
_AT_SEARCH_RESULT = variable_from_module(*settings.SEARCH_AT_RESULT.rsplit('.', 1))
|
||||
return _AT_SEARCH_RESULT(results, caller, query=object_name)
|
||||
elif len(results) == 1: # A unique local match
|
||||
obj = results[0]
|
||||
else: # No matches. Search globally
|
||||
obj = caller.search(object_name, global_search=True)
|
||||
if not obj:
|
||||
return
|
||||
|
||||
if self.rhs:
|
||||
# this means a target name was given
|
||||
|
|
@ -2854,7 +2861,7 @@ class CmdSpawn(COMMAND_DEFAULT_CLASS):
|
|||
|
||||
key = "@spawn"
|
||||
aliases = ["olc"]
|
||||
switch_options = ("noloc", "search", "list", "show", "save", "delete", "menu", "olc", "update")
|
||||
switch_options = ("noloc", "search", "list", "show", "examine", "save", "delete", "menu", "olc", "update", "edit")
|
||||
locks = "cmd:perm(spawn) or perm(Builder)"
|
||||
help_category = "Building"
|
||||
|
||||
|
|
@ -2905,12 +2912,13 @@ class CmdSpawn(COMMAND_DEFAULT_CLASS):
|
|||
|
||||
caller = self.caller
|
||||
|
||||
if self.cmdstring == "olc" or 'menu' in self.switches or 'olc' in self.switches:
|
||||
if self.cmdstring == "olc" or 'menu' in self.switches \
|
||||
or 'olc' in self.switches or 'edit' in self.switches:
|
||||
# OLC menu mode
|
||||
prototype = None
|
||||
if self.lhs:
|
||||
key = self.lhs
|
||||
prototype = spawner.search_prototype(key=key, return_meta=True)
|
||||
prototype = protlib.search_prototype(key=key)
|
||||
if len(prototype) > 1:
|
||||
caller.msg("More than one match for {}:\n{}".format(
|
||||
key, "\n".join(proto.get('prototype_key', '') for proto in prototype)))
|
||||
|
|
@ -2918,6 +2926,10 @@ class CmdSpawn(COMMAND_DEFAULT_CLASS):
|
|||
elif prototype:
|
||||
# one match
|
||||
prototype = prototype[0]
|
||||
else:
|
||||
# no match
|
||||
caller.msg("No prototype '{}' was found.".format(key))
|
||||
return
|
||||
olc_menus.start_olc(caller, session=self.session, prototype=prototype)
|
||||
return
|
||||
|
||||
|
|
@ -3034,7 +3046,7 @@ class CmdSpawn(COMMAND_DEFAULT_CLASS):
|
|||
caller.msg("|rDeletion cancelled.|n")
|
||||
return
|
||||
try:
|
||||
success = protlib.delete_db_prototype(caller, self.args)
|
||||
success = protlib.delete_prototype(self.args)
|
||||
except protlib.PermissionError as err:
|
||||
caller.msg("|rError deleting:|R {}|n".format(err))
|
||||
caller.msg("Deletion {}.".format(
|
||||
|
|
@ -3084,7 +3096,8 @@ class CmdSpawn(COMMAND_DEFAULT_CLASS):
|
|||
return
|
||||
# we have a prototype, check access
|
||||
prototype = prototypes[0]
|
||||
if not caller.locks.check_lockstring(caller, prototype.get('prototype_locks', ''), access_type='spawn'):
|
||||
if not caller.locks.check_lockstring(
|
||||
caller, prototype.get('prototype_locks', ''), access_type='spawn', default=True):
|
||||
caller.msg("You don't have access to use this prototype.")
|
||||
return
|
||||
|
||||
|
|
|
|||
|
|
@ -57,6 +57,7 @@ class CharacterCmdSet(CmdSet):
|
|||
self.add(admin.CmdEmit())
|
||||
self.add(admin.CmdPerm())
|
||||
self.add(admin.CmdWall())
|
||||
self.add(admin.CmdForce())
|
||||
|
||||
# Building and world manipulation
|
||||
self.add(building.CmdTeleport())
|
||||
|
|
|
|||
|
|
@ -66,7 +66,7 @@ class CmdHelp(Command):
|
|||
if type(self).help_more:
|
||||
usemore = True
|
||||
|
||||
if self.session.protocol_key in ("websocket", "ajax/comet"):
|
||||
if self.session and self.session.protocol_key in ("websocket", "ajax/comet"):
|
||||
try:
|
||||
options = self.account.db._saved_webclient_options
|
||||
if options and options["helppopup"]:
|
||||
|
|
|
|||
|
|
@ -47,6 +47,7 @@ class CmdReload(COMMAND_DEFAULT_CLASS):
|
|||
@reset to purge) and at_reload() hooks will be called.
|
||||
"""
|
||||
key = "@reload"
|
||||
aliases = ['@restart']
|
||||
locks = "cmd:perm(reload) or perm(Developer)"
|
||||
help_category = "System"
|
||||
|
||||
|
|
@ -710,6 +711,7 @@ class CmdTime(COMMAND_DEFAULT_CLASS):
|
|||
"""Show server time data in a table."""
|
||||
table1 = EvTable("|wServer time", "", align="l", width=78)
|
||||
table1.add_row("Current uptime", utils.time_format(gametime.uptime(), 3))
|
||||
table1.add_row("Portal uptime", utils.time_format(gametime.portal_uptime(), 3))
|
||||
table1.add_row("Total runtime", utils.time_format(gametime.runtime(), 2))
|
||||
table1.add_row("First start", datetime.datetime.fromtimestamp(gametime.server_epoch()))
|
||||
table1.add_row("Current time", datetime.datetime.now())
|
||||
|
|
|
|||
|
|
@ -243,6 +243,9 @@ class TestAdmin(CommandTest):
|
|||
def test_ban(self):
|
||||
self.call(admin.CmdBan(), "Char", "Name-Ban char was added.")
|
||||
|
||||
def test_force(self):
|
||||
self.call(admin.CmdForce(), "Char2=say test", 'Char2(#7) says, "test"|You have forced Char2 to: say test')
|
||||
|
||||
|
||||
class TestAccount(CommandTest):
|
||||
|
||||
|
|
@ -280,6 +283,32 @@ class TestAccount(CommandTest):
|
|||
def test_char_create(self):
|
||||
self.call(account.CmdCharCreate(), "Test1=Test char",
|
||||
"Created new character Test1. Use @ic Test1 to enter the game", caller=self.account)
|
||||
|
||||
def test_char_delete(self):
|
||||
# Chardelete requires user input; this test is mainly to confirm
|
||||
# whether permissions are being checked
|
||||
|
||||
# Add char to account playable characters
|
||||
self.account.db._playable_characters.append(self.char1)
|
||||
|
||||
# Try deleting as Developer
|
||||
self.call(account.CmdCharDelete(), "Char", "This will permanently destroy 'Char'. This cannot be undone. Continue yes/[no]?", caller=self.account)
|
||||
|
||||
# Downgrade permissions on account
|
||||
self.account.permissions.add('Player')
|
||||
self.account.permissions.remove('Developer')
|
||||
|
||||
# Set lock on character object to prevent deletion
|
||||
self.char1.locks.add('delete:none()')
|
||||
|
||||
# Try deleting as Player
|
||||
self.call(account.CmdCharDelete(), "Char", "You do not have permission to delete this character.", caller=self.account)
|
||||
|
||||
# Set lock on character object to allow self-delete
|
||||
self.char1.locks.add('delete:pid(%i)' % self.account.id)
|
||||
|
||||
# Try deleting as Player again
|
||||
self.call(account.CmdCharDelete(), "Char", "This will permanently destroy 'Char'. This cannot be undone. Continue yes/[no]?", caller=self.account)
|
||||
|
||||
def test_quell(self):
|
||||
self.call(account.CmdQuell(), "", "Quelling to current puppet's permissions (developer).", caller=self.account)
|
||||
|
|
@ -315,6 +344,24 @@ class TestBuilding(CommandTest):
|
|||
def test_desc(self):
|
||||
self.call(building.CmdDesc(), "Obj2=TestDesc", "The description was set on Obj2(#5).")
|
||||
|
||||
def test_empty_desc(self):
|
||||
"""
|
||||
empty desc sets desc as ''
|
||||
"""
|
||||
o2d = self.obj2.db.desc
|
||||
r1d = self.room1.db.desc
|
||||
self.call(building.CmdDesc(), "Obj2=", "The description was set on Obj2(#5).")
|
||||
assert self.obj2.db.desc == '' and self.obj2.db.desc != o2d
|
||||
assert self.room1.db.desc == r1d
|
||||
|
||||
def test_desc_default_to_room(self):
|
||||
"""no rhs changes room's desc"""
|
||||
o2d = self.obj2.db.desc
|
||||
r1d = self.room1.db.desc
|
||||
self.call(building.CmdDesc(), "Obj2", "The description was set on Room(#1).")
|
||||
assert self.obj2.db.desc == o2d
|
||||
assert self.room1.db.desc == 'Obj2' and self.room1.db.desc != r1d
|
||||
|
||||
def test_wipe(self):
|
||||
confirm = building.CmdDestroy.confirm
|
||||
building.CmdDestroy.confirm = False
|
||||
|
|
@ -334,6 +381,14 @@ class TestBuilding(CommandTest):
|
|||
self.call(building.CmdOpen(), "TestExit1=Room2", "Created new Exit 'TestExit1' from Room to Room2")
|
||||
self.call(building.CmdLink(), "TestExit1=Room", "Link created TestExit1 -> Room (one way).")
|
||||
self.call(building.CmdUnLink(), "TestExit1", "Former exit TestExit1 no longer links anywhere.")
|
||||
self.char1.location = self.room2
|
||||
self.call(building.CmdOpen(), "TestExit2=Room", "Created new Exit 'TestExit2' from Room2 to Room.")
|
||||
# ensure it matches locally first
|
||||
self.call(building.CmdLink(), "TestExit=Room2", "Link created TestExit2 -> Room2 (one way).")
|
||||
# ensure can still match globally when not a local name
|
||||
self.call(building.CmdLink(), "TestExit1=Room2", "Note: TestExit1(#8) did not have a destination set before. "
|
||||
"Make sure you linked the right thing.\n"
|
||||
"Link created TestExit1 -> Room2 (one way).")
|
||||
|
||||
def test_set_home(self):
|
||||
self.call(building.CmdSetHome(), "Obj = Room2", "Obj's home location was changed from Room")
|
||||
|
|
@ -460,6 +515,61 @@ class TestBuilding(CommandTest):
|
|||
# Test listing commands
|
||||
self.call(building.CmdSpawn(), "/list", "Key ")
|
||||
|
||||
# @spawn/edit (missing prototype)
|
||||
# brings up olc menu
|
||||
msg = self.call(
|
||||
building.CmdSpawn(),
|
||||
'/edit')
|
||||
assert 'Prototype wizard' in msg
|
||||
|
||||
# @spawn/edit with valid prototype
|
||||
# brings up olc menu loaded with prototype
|
||||
msg = self.call(
|
||||
building.CmdSpawn(),
|
||||
'/edit testball')
|
||||
assert 'Prototype wizard' in msg
|
||||
assert hasattr(self.char1.ndb._menutree, "olc_prototype")
|
||||
assert dict == type(self.char1.ndb._menutree.olc_prototype) \
|
||||
and 'prototype_key' in self.char1.ndb._menutree.olc_prototype \
|
||||
and 'key' in self.char1.ndb._menutree.olc_prototype \
|
||||
and 'testball' == self.char1.ndb._menutree.olc_prototype['prototype_key'] \
|
||||
and 'Ball' == self.char1.ndb._menutree.olc_prototype['key']
|
||||
assert 'Ball' in msg and 'testball' in msg
|
||||
|
||||
# @spawn/edit with valid prototype (synomym)
|
||||
msg = self.call(
|
||||
building.CmdSpawn(),
|
||||
'/edit BALL')
|
||||
assert 'Prototype wizard' in msg
|
||||
assert 'Ball' in msg and 'testball' in msg
|
||||
|
||||
# @spawn/edit with invalid prototype
|
||||
msg = self.call(
|
||||
building.CmdSpawn(),
|
||||
'/edit NO_EXISTS',
|
||||
"No prototype 'NO_EXISTS' was found.")
|
||||
|
||||
# @spawn/examine (missing prototype)
|
||||
# lists all prototypes that exist
|
||||
msg = self.call(
|
||||
building.CmdSpawn(),
|
||||
'/examine')
|
||||
assert 'testball' in msg and 'testprot' in msg
|
||||
|
||||
# @spawn/examine with valid prototype
|
||||
# prints the prototype
|
||||
msg = self.call(
|
||||
building.CmdSpawn(),
|
||||
'/examine BALL')
|
||||
assert 'Ball' in msg and 'testball' in msg
|
||||
|
||||
# @spawn/examine with invalid prototype
|
||||
# shows error
|
||||
self.call(
|
||||
building.CmdSpawn(),
|
||||
'/examine NO_EXISTS',
|
||||
"No prototype 'NO_EXISTS' was found.")
|
||||
|
||||
|
||||
class TestComms(CommandTest):
|
||||
|
||||
|
|
|
|||
|
|
@ -4,17 +4,11 @@ Commands that are available from the connect screen.
|
|||
import re
|
||||
import datetime
|
||||
from codecs import lookup as codecs_lookup
|
||||
from random import getrandbits
|
||||
from django.conf import settings
|
||||
from django.contrib.auth import authenticate
|
||||
from evennia.accounts.models import AccountDB
|
||||
from evennia.objects.models import ObjectDB
|
||||
from evennia.server.models import ServerConfig
|
||||
from evennia.server.throttle import Throttle
|
||||
from evennia.comms.models import ChannelDB
|
||||
from evennia.server.sessionhandler import SESSIONS
|
||||
|
||||
from evennia.utils import create, logger, utils, gametime
|
||||
from evennia.utils import class_from_module, create, logger, utils, gametime
|
||||
from evennia.commands.cmdhandler import CMD_LOGINSTART
|
||||
|
||||
COMMAND_DEFAULT_CLASS = utils.class_from_module(settings.COMMAND_DEFAULT_CLASS)
|
||||
|
|
@ -26,11 +20,6 @@ __all__ = ("CmdUnconnectedConnect", "CmdUnconnectedCreate",
|
|||
MULTISESSION_MODE = settings.MULTISESSION_MODE
|
||||
CONNECTION_SCREEN_MODULE = settings.CONNECTION_SCREEN_MODULE
|
||||
|
||||
# Create throttles for too many connections, account-creations and login attempts
|
||||
CONNECTION_THROTTLE = Throttle(limit=5, timeout=1 * 60)
|
||||
CREATION_THROTTLE = Throttle(limit=2, timeout=10 * 60)
|
||||
LOGIN_THROTTLE = Throttle(limit=5, timeout=5 * 60)
|
||||
|
||||
|
||||
def create_guest_account(session):
|
||||
"""
|
||||
|
|
@ -44,49 +33,20 @@ def create_guest_account(session):
|
|||
the boolean is whether guest accounts are enabled at all.
|
||||
the Account which was created from an available guest name.
|
||||
"""
|
||||
# check if guests are enabled.
|
||||
if not settings.GUEST_ENABLED:
|
||||
return False, None
|
||||
enabled = settings.GUEST_ENABLED
|
||||
address = session.address
|
||||
|
||||
# Check IP bans.
|
||||
bans = ServerConfig.objects.conf("server_bans")
|
||||
if bans and any(tup[2].match(session.address) for tup in bans if tup[2]):
|
||||
# this is a banned IP!
|
||||
string = "|rYou have been banned and cannot continue from here." \
|
||||
"\nIf you feel this ban is in error, please email an admin.|x"
|
||||
session.msg(string)
|
||||
session.sessionhandler.disconnect(session, "Good bye! Disconnecting.")
|
||||
return True, None
|
||||
# Get account class
|
||||
Guest = class_from_module(settings.BASE_GUEST_TYPECLASS)
|
||||
|
||||
try:
|
||||
# Find an available guest name.
|
||||
accountname = None
|
||||
for name in settings.GUEST_LIST:
|
||||
if not AccountDB.objects.filter(username__iexact=accountname).count():
|
||||
accountname = name
|
||||
break
|
||||
if not accountname:
|
||||
session.msg("All guest accounts are in use. Please try again later.")
|
||||
return True, None
|
||||
else:
|
||||
# build a new account with the found guest accountname
|
||||
password = "%016x" % getrandbits(64)
|
||||
home = ObjectDB.objects.get_id(settings.GUEST_HOME)
|
||||
permissions = settings.PERMISSION_GUEST_DEFAULT
|
||||
typeclass = settings.BASE_CHARACTER_TYPECLASS
|
||||
ptypeclass = settings.BASE_GUEST_TYPECLASS
|
||||
new_account = _create_account(session, accountname, password, permissions, ptypeclass)
|
||||
if new_account:
|
||||
_create_character(session, new_account, typeclass, home, permissions)
|
||||
return True, new_account
|
||||
|
||||
except Exception:
|
||||
# We are in the middle between logged in and -not, so we have
|
||||
# to handle tracebacks ourselves at this point. If we don't,
|
||||
# we won't see any errors at all.
|
||||
session.msg("An error occurred. Please e-mail an admin if the problem persists.")
|
||||
logger.log_trace()
|
||||
raise
|
||||
# Get an available guest account
|
||||
# authenticate() handles its own throttling
|
||||
account, errors = Guest.authenticate(ip=address)
|
||||
if account:
|
||||
return enabled, account
|
||||
else:
|
||||
session.msg("|R%s|n" % '\n'.join(errors))
|
||||
return enabled, None
|
||||
|
||||
|
||||
def create_normal_account(session, name, password):
|
||||
|
|
@ -101,38 +61,17 @@ def create_normal_account(session, name, password):
|
|||
Returns:
|
||||
account (Account): the account which was created from the name and password.
|
||||
"""
|
||||
# check for too many login errors too quick.
|
||||
address = session.address
|
||||
if isinstance(address, tuple):
|
||||
address = address[0]
|
||||
# Get account class
|
||||
Account = class_from_module(settings.BASE_ACCOUNT_TYPECLASS)
|
||||
|
||||
if LOGIN_THROTTLE.check(address):
|
||||
session.msg("|RYou made too many connection attempts. Try again in a few minutes.|n")
|
||||
return None
|
||||
address = session.address
|
||||
|
||||
# Match account name and check password
|
||||
account = authenticate(username=name, password=password)
|
||||
|
||||
# authenticate() handles all its own throttling
|
||||
account, errors = Account.authenticate(username=name, password=password, ip=address, session=session)
|
||||
if not account:
|
||||
# No accountname or password match
|
||||
session.msg("Incorrect login information given.")
|
||||
# this just updates the throttle
|
||||
LOGIN_THROTTLE.update(address)
|
||||
# calls account hook for a failed login if possible.
|
||||
account = AccountDB.objects.get_account_from_name(name)
|
||||
if account:
|
||||
account.at_failed_login(session)
|
||||
return None
|
||||
|
||||
# Check IP and/or name bans
|
||||
bans = ServerConfig.objects.conf("server_bans")
|
||||
if bans and (any(tup[0] == account.name.lower() for tup in bans) or
|
||||
any(tup[2].match(session.address) for tup in bans if tup[2])):
|
||||
# this is a banned IP or name!
|
||||
string = "|rYou have been banned and cannot continue from here." \
|
||||
"\nIf you feel this ban is in error, please email an admin.|x"
|
||||
session.msg(string)
|
||||
session.sessionhandler.disconnect(session, "Good bye! Disconnecting.")
|
||||
session.msg("|R%s|n" % '\n'.join(errors))
|
||||
return None
|
||||
|
||||
return account
|
||||
|
|
@ -164,15 +103,7 @@ class CmdUnconnectedConnect(COMMAND_DEFAULT_CLASS):
|
|||
there is no object yet before the account has logged in)
|
||||
"""
|
||||
session = self.caller
|
||||
|
||||
# check for too many login errors too quick.
|
||||
address = session.address
|
||||
if isinstance(address, tuple):
|
||||
address = address[0]
|
||||
if CONNECTION_THROTTLE.check(address):
|
||||
# timeout is 5 minutes.
|
||||
session.msg("|RYou made too many connection attempts. Try again in a few minutes.|n")
|
||||
return
|
||||
|
||||
args = self.args
|
||||
# extract double quote parts
|
||||
|
|
@ -180,23 +111,33 @@ class CmdUnconnectedConnect(COMMAND_DEFAULT_CLASS):
|
|||
if len(parts) == 1:
|
||||
# this was (hopefully) due to no double quotes being found, or a guest login
|
||||
parts = parts[0].split(None, 1)
|
||||
|
||||
# Guest login
|
||||
if len(parts) == 1 and parts[0].lower() == "guest":
|
||||
enabled, new_account = create_guest_account(session)
|
||||
if new_account:
|
||||
session.sessionhandler.login(session, new_account)
|
||||
if enabled:
|
||||
# Get Guest typeclass
|
||||
Guest = class_from_module(settings.BASE_GUEST_TYPECLASS)
|
||||
|
||||
account, errors = Guest.authenticate(ip=address)
|
||||
if account:
|
||||
session.sessionhandler.login(session, account)
|
||||
return
|
||||
else:
|
||||
session.msg("|R%s|n" % '\n'.join(errors))
|
||||
return
|
||||
|
||||
if len(parts) != 2:
|
||||
session.msg("\n\r Usage (without <>): connect <name> <password>")
|
||||
return
|
||||
|
||||
CONNECTION_THROTTLE.update(address)
|
||||
# Get account class
|
||||
Account = class_from_module(settings.BASE_ACCOUNT_TYPECLASS)
|
||||
|
||||
name, password = parts
|
||||
account = create_normal_account(session, name, password)
|
||||
account, errors = Account.authenticate(username=name, password=password, ip=address, session=session)
|
||||
if account:
|
||||
session.sessionhandler.login(session, account)
|
||||
else:
|
||||
session.msg("|R%s|n" % '\n'.join(errors))
|
||||
|
||||
|
||||
class CmdUnconnectedCreate(COMMAND_DEFAULT_CLASS):
|
||||
|
|
@ -222,14 +163,10 @@ class CmdUnconnectedCreate(COMMAND_DEFAULT_CLASS):
|
|||
session = self.caller
|
||||
args = self.args.strip()
|
||||
|
||||
# Rate-limit account creation.
|
||||
address = session.address
|
||||
|
||||
if isinstance(address, tuple):
|
||||
address = address[0]
|
||||
if CREATION_THROTTLE.check(address):
|
||||
session.msg("|RYou are creating too many accounts. Try again in a few minutes.|n")
|
||||
return
|
||||
# Get account class
|
||||
Account = class_from_module(settings.BASE_ACCOUNT_TYPECLASS)
|
||||
|
||||
# extract double quoted parts
|
||||
parts = [part.strip() for part in re.split(r"\"", args) if part.strip()]
|
||||
|
|
@ -241,77 +178,21 @@ class CmdUnconnectedCreate(COMMAND_DEFAULT_CLASS):
|
|||
"\nIf <name> or <password> contains spaces, enclose it in double quotes."
|
||||
session.msg(string)
|
||||
return
|
||||
accountname, password = parts
|
||||
|
||||
# sanity checks
|
||||
if not re.findall(r"^[\w. @+\-']+$", accountname) or not (0 < len(accountname) <= 30):
|
||||
# this echoes the restrictions made by django's auth
|
||||
# module (except not allowing spaces, for convenience of
|
||||
# logging in).
|
||||
string = "\n\r Accountname can max be 30 characters or fewer. Letters, spaces, digits and @/./+/-/_/' only."
|
||||
session.msg(string)
|
||||
return
|
||||
# strip excessive spaces in accountname
|
||||
accountname = re.sub(r"\s+", " ", accountname).strip()
|
||||
if AccountDB.objects.filter(username__iexact=accountname):
|
||||
# account already exists (we also ignore capitalization here)
|
||||
session.msg("Sorry, there is already an account with the name '%s'." % accountname)
|
||||
return
|
||||
# Reserve accountnames found in GUEST_LIST
|
||||
if settings.GUEST_LIST and accountname.lower() in (guest.lower() for guest in settings.GUEST_LIST):
|
||||
string = "\n\r That name is reserved. Please choose another Accountname."
|
||||
session.msg(string)
|
||||
return
|
||||
|
||||
# Validate password
|
||||
Account = utils.class_from_module(settings.BASE_ACCOUNT_TYPECLASS)
|
||||
# Have to create a dummy Account object to check username similarity
|
||||
valid, error = Account.validate_password(password, account=Account(username=accountname))
|
||||
if error:
|
||||
errors = [e for suberror in error.messages for e in error.messages]
|
||||
string = "\n".join(errors)
|
||||
session.msg(string)
|
||||
return
|
||||
|
||||
# Check IP and/or name bans
|
||||
bans = ServerConfig.objects.conf("server_bans")
|
||||
if bans and (any(tup[0] == accountname.lower() for tup in bans) or
|
||||
|
||||
any(tup[2].match(session.address) for tup in bans if tup[2])):
|
||||
# this is a banned IP or name!
|
||||
string = "|rYou have been banned and cannot continue from here." \
|
||||
"\nIf you feel this ban is in error, please email an admin.|x"
|
||||
session.msg(string)
|
||||
session.sessionhandler.disconnect(session, "Good bye! Disconnecting.")
|
||||
return
|
||||
username, password = parts
|
||||
|
||||
# everything's ok. Create the new account account.
|
||||
try:
|
||||
permissions = settings.PERMISSION_ACCOUNT_DEFAULT
|
||||
typeclass = settings.BASE_CHARACTER_TYPECLASS
|
||||
new_account = _create_account(session, accountname, password, permissions)
|
||||
if new_account:
|
||||
if MULTISESSION_MODE < 2:
|
||||
default_home = ObjectDB.objects.get_id(settings.DEFAULT_HOME)
|
||||
_create_character(session, new_account, typeclass, default_home, permissions)
|
||||
|
||||
# Update the throttle to indicate a new account was created from this IP
|
||||
CREATION_THROTTLE.update(address)
|
||||
|
||||
# tell the caller everything went well.
|
||||
string = "A new account '%s' was created. Welcome!"
|
||||
if " " in accountname:
|
||||
string += "\n\nYou can now log in with the command 'connect \"%s\" <your password>'."
|
||||
else:
|
||||
string += "\n\nYou can now log with the command 'connect %s <your password>'."
|
||||
session.msg(string % (accountname, accountname))
|
||||
|
||||
except Exception:
|
||||
# We are in the middle between logged in and -not, so we have
|
||||
# to handle tracebacks ourselves at this point. If we don't,
|
||||
# we won't see any errors at all.
|
||||
session.msg("An error occurred. Please e-mail an admin if the problem persists.")
|
||||
logger.log_trace()
|
||||
account, errors = Account.create(username=username, password=password, ip=address, session=session)
|
||||
if account:
|
||||
# tell the caller everything went well.
|
||||
string = "A new account '%s' was created. Welcome!"
|
||||
if " " in username:
|
||||
string += "\n\nYou can now log in with the command 'connect \"%s\" <your password>'."
|
||||
else:
|
||||
string += "\n\nYou can now log with the command 'connect %s <your password>'."
|
||||
session.msg(string % (username, username))
|
||||
else:
|
||||
session.msg("|R%s|n" % '\n'.join(errors))
|
||||
|
||||
|
||||
class CmdUnconnectedQuit(COMMAND_DEFAULT_CLASS):
|
||||
|
|
@ -395,6 +276,9 @@ Next you can connect to the game: |wconnect Anna c67jHL8p|n
|
|||
You can use the |wlook|n command if you want to see the connect screen again.
|
||||
|
||||
"""
|
||||
|
||||
if settings.STAFF_CONTACT_EMAIL:
|
||||
string += 'For support, please contact: %s' % settings.STAFF_CONTACT_EMAIL
|
||||
self.caller.msg(string)
|
||||
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue