Fix merge conflicts
This commit is contained in:
commit
90b42ca6fb
47 changed files with 1682 additions and 201 deletions
|
|
@ -1,4 +1,5 @@
|
||||||
language: python
|
language: python
|
||||||
|
cache: pip
|
||||||
python:
|
python:
|
||||||
- "3.6"
|
- "3.6"
|
||||||
sudo: false
|
sudo: false
|
||||||
|
|
|
||||||
18
CHANGELOG.md
18
CHANGELOG.md
|
|
@ -58,6 +58,15 @@ Web/Django standard initiative (@strikaco)
|
||||||
- Bugfixes
|
- Bugfixes
|
||||||
- Fixes bug on login page where error messages were not being displayed
|
- Fixes bug on login page where error messages were not being displayed
|
||||||
|
|
||||||
|
### Prototypes
|
||||||
|
|
||||||
|
- `evennia.prototypes.save_prototype` now takes the prototype as a normal
|
||||||
|
argument (`prototype`) instead of having to give it as `**prototype`.
|
||||||
|
- `evennia.prototypes.search_prototype` has a new kwarg `require_single=False` that
|
||||||
|
raises a KeyError exception if query gave 0 or >1 results.
|
||||||
|
- `evennia.prototypes.spawner` can now spawn by passing a `prototype_key`
|
||||||
|
|
||||||
|
|
||||||
### Typeclasses
|
### Typeclasses
|
||||||
|
|
||||||
- Add new methods on all typeclasses, useful specifically for object handling from the website/admin:
|
- Add new methods on all typeclasses, useful specifically for object handling from the website/admin:
|
||||||
|
|
@ -81,7 +90,9 @@ Web/Django standard initiative (@strikaco)
|
||||||
|
|
||||||
### Utils
|
### Utils
|
||||||
|
|
||||||
- Added more unit tests.
|
- `evennia` launcher now fully handles all django-admin commands, like running tests in parallel.
|
||||||
|
- `evennia.utils.create.account` now also takes `tags` and `attrs` keywords.
|
||||||
|
- Added many more unit tests.
|
||||||
|
|
||||||
### Server
|
### Server
|
||||||
|
|
||||||
|
|
@ -213,6 +224,11 @@ Web/Django standard initiative (@strikaco)
|
||||||
- `tb_range` - Adds system for abstract positioning and movement.
|
- `tb_range` - Adds system for abstract positioning and movement.
|
||||||
- Updates and some cleanup of existing contribs.
|
- Updates and some cleanup of existing contribs.
|
||||||
|
|
||||||
|
|
||||||
|
### Internationalization
|
||||||
|
|
||||||
|
- Polish translation by user ogotai
|
||||||
|
|
||||||
# Overviews
|
# Overviews
|
||||||
|
|
||||||
## Sept 2017:
|
## Sept 2017:
|
||||||
|
|
|
||||||
|
|
@ -33,7 +33,7 @@ LABEL maintainer="www.evennia.com"
|
||||||
|
|
||||||
# install compilation environment
|
# install compilation environment
|
||||||
RUN apk update && apk add bash gcc jpeg-dev musl-dev procps py-pip \
|
RUN apk update && apk add bash gcc jpeg-dev musl-dev procps py-pip \
|
||||||
py-setuptools py2-openssl python python-dev zlib-dev
|
py-setuptools py2-openssl python python-dev zlib-dev gettext
|
||||||
|
|
||||||
# add the files required for pip installation
|
# add the files required for pip installation
|
||||||
COPY ./setup.py /usr/src/evennia/
|
COPY ./setup.py /usr/src/evennia/
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,9 @@
|
||||||
"""
|
"""
|
||||||
Evennia MUD/MUX/MU* creation system
|
Evennia MUD/MUX/MU* creation system
|
||||||
|
|
||||||
This is the main top-level API for Evennia. You can also explore the
|
This is the main top-level API for Evennia. You can explore the evennia library
|
||||||
evennia library by accessing evennia.<subpackage> directly. From
|
by accessing evennia.<subpackage> directly. From inside the game you can read
|
||||||
inside the game you can read docs of all object by viewing its
|
docs of all object by viewing its `__doc__` string, such as through
|
||||||
`__doc__` string, such as through
|
|
||||||
|
|
||||||
@py evennia.ObjectDB.__doc__
|
@py evennia.ObjectDB.__doc__
|
||||||
|
|
||||||
|
|
@ -18,6 +17,31 @@ See www.evennia.com for full documentation.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
# docstring header
|
||||||
|
|
||||||
|
DOCSTRING = """
|
||||||
|
|cEvennia|n 'flat' API (use |wevennia.<component>.__doc__|n to read doc-strings
|
||||||
|
and |wdict(evennia.component)|n or
|
||||||
|
|wevennia.component.__dict__ to see contents)
|
||||||
|
|cTypeclass-bases:|n |cDatabase models|n:
|
||||||
|
DefaultAccount DefaultObject AccountDB ObjectDB
|
||||||
|
DefaultGuest DefaultCharacter ChannelDB
|
||||||
|
DefaultRoom ScriptDB
|
||||||
|
DefaultChannel DefaultExit Msg
|
||||||
|
DefaultScript
|
||||||
|
|cSearch functions:|n |cCommand parents and helpers:|n
|
||||||
|
search_account search_object default_cmds
|
||||||
|
search_script search_channel Command InterruptCommand
|
||||||
|
search_help search_message CmdSet
|
||||||
|
search_tag managers |cUtilities:|n
|
||||||
|
|cCreate functions:|n settings lockfuncs
|
||||||
|
create_account create_object logger gametime
|
||||||
|
create_script create_channel ansi spawn
|
||||||
|
create_help_entry create_message contrib managers
|
||||||
|
|cGlobal handlers:|n set_trace
|
||||||
|
TICKER_HANDLER TASK_HANDLER EvMenu EvTable
|
||||||
|
SESSION_HANDLER CHANNEL_HANDLER EvForm EvEditor """
|
||||||
|
|
||||||
# Delayed loading of properties
|
# Delayed loading of properties
|
||||||
|
|
||||||
# Typeclasses
|
# Typeclasses
|
||||||
|
|
@ -114,7 +138,6 @@ def _create_version():
|
||||||
__version__ = _create_version()
|
__version__ = _create_version()
|
||||||
del _create_version
|
del _create_version
|
||||||
|
|
||||||
|
|
||||||
def _init():
|
def _init():
|
||||||
"""
|
"""
|
||||||
This function is called automatically by the launcher only after
|
This function is called automatically by the launcher only after
|
||||||
|
|
@ -188,6 +211,10 @@ def _init():
|
||||||
from .comms.channelhandler import CHANNEL_HANDLER
|
from .comms.channelhandler import CHANNEL_HANDLER
|
||||||
from .scripts.monitorhandler import MONITOR_HANDLER
|
from .scripts.monitorhandler import MONITOR_HANDLER
|
||||||
|
|
||||||
|
# initialize the doc string
|
||||||
|
global __doc__
|
||||||
|
__doc__ = ansi.parse_ansi(DOCSTRING)
|
||||||
|
|
||||||
# API containers
|
# API containers
|
||||||
|
|
||||||
class _EvContainer(object):
|
class _EvContainer(object):
|
||||||
|
|
@ -205,15 +232,17 @@ def _init():
|
||||||
|
|
||||||
class DBmanagers(_EvContainer):
|
class DBmanagers(_EvContainer):
|
||||||
"""
|
"""
|
||||||
Links to instantiated database managers.
|
Links to instantiated Django database managers. These are used
|
||||||
|
to perform more advanced custom database queries than the standard
|
||||||
|
search functions allow.
|
||||||
|
|
||||||
helpentry - HelpEntry.objects
|
helpentries - HelpEntry.objects
|
||||||
accounts - AccountDB.objects
|
accounts - AccountDB.objects
|
||||||
scripts - ScriptDB.objects
|
scripts - ScriptDB.objects
|
||||||
msgs - Msg.objects
|
msgs - Msg.objects
|
||||||
channels - Channel.objects
|
channels - Channel.objects
|
||||||
objects - ObjectDB.objects
|
objects - ObjectDB.objects
|
||||||
serverconfigs = ServerConfig.objects
|
serverconfigs - ServerConfig.objects
|
||||||
tags - Tags.objects
|
tags - Tags.objects
|
||||||
attributes - Attributes.objects
|
attributes - Attributes.objects
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1333,19 +1333,28 @@ class DefaultAccount(with_metaclass(TypeclassBase, AccountDB)):
|
||||||
# list of targets - make list to disconnect from db
|
# list of targets - make list to disconnect from db
|
||||||
characters = list(tar for tar in target if tar) if target else []
|
characters = list(tar for tar in target if tar) if target else []
|
||||||
sessions = self.sessions.all()
|
sessions = self.sessions.all()
|
||||||
|
if not sessions:
|
||||||
|
# no sessions, nothing to report
|
||||||
|
return ""
|
||||||
is_su = self.is_superuser
|
is_su = self.is_superuser
|
||||||
|
|
||||||
# text shown when looking in the ooc area
|
# text shown when looking in the ooc area
|
||||||
result = ["Account |g%s|n (you are Out-of-Character)" % self.key]
|
result = ["Account |g%s|n (you are Out-of-Character)" % self.key]
|
||||||
|
|
||||||
nsess = len(sessions)
|
nsess = len(sessions)
|
||||||
result.append(nsess == 1 and "\n\n|wConnected session:|n" or "\n\n|wConnected sessions (%i):|n" % nsess)
|
result.append(nsess == 1 and
|
||||||
|
"\n\n|wConnected session:|n" or
|
||||||
|
"\n\n|wConnected sessions (%i):|n" % nsess)
|
||||||
for isess, sess in enumerate(sessions):
|
for isess, sess in enumerate(sessions):
|
||||||
csessid = sess.sessid
|
csessid = sess.sessid
|
||||||
addr = "%s (%s)" % (sess.protocol_key, isinstance(sess.address, tuple) and
|
addr = "%s (%s)" % (sess.protocol_key, isinstance(sess.address, tuple) and
|
||||||
str(sess.address[0]) or str(sess.address))
|
str(sess.address[0]) or
|
||||||
result.append("\n %s %s" % (session.sessid == csessid and "|w* %s|n" % (isess + 1) or
|
str(sess.address))
|
||||||
" %s" % (isess + 1), addr))
|
result.append("\n %s %s" % (
|
||||||
|
session and
|
||||||
|
session.sessid == csessid and
|
||||||
|
"|w* %s|n" % (isess + 1) or
|
||||||
|
" %s" % (isess + 1), addr))
|
||||||
result.append("\n\n |whelp|n - more commands")
|
result.append("\n\n |whelp|n - more commands")
|
||||||
result.append("\n |wooc <Text>|n - talk on public channel")
|
result.append("\n |wooc <Text>|n - talk on public channel")
|
||||||
|
|
||||||
|
|
@ -1487,7 +1496,6 @@ class DefaultGuest(DefaultAccount):
|
||||||
characters = self.db._playable_characters
|
characters = self.db._playable_characters
|
||||||
for character in characters:
|
for character in characters:
|
||||||
if character:
|
if character:
|
||||||
print("deleting Character:", character)
|
|
||||||
character.delete()
|
character.delete()
|
||||||
|
|
||||||
def at_post_disconnect(self, **kwargs):
|
def at_post_disconnect(self, **kwargs):
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
from mock import Mock, MagicMock
|
import sys
|
||||||
|
from mock import Mock, MagicMock, patch
|
||||||
from random import randint
|
from random import randint
|
||||||
from unittest import TestCase
|
from unittest import TestCase
|
||||||
|
|
||||||
|
|
@ -60,19 +61,20 @@ class TestAccountSessionHandler(TestCase):
|
||||||
"Check count method"
|
"Check count method"
|
||||||
self.assertEqual(self.handler.count(), len(self.handler.get()))
|
self.assertEqual(self.handler.count(), len(self.handler.get()))
|
||||||
|
|
||||||
|
|
||||||
|
@override_settings(GUEST_ENABLED=True, GUEST_LIST=["bruce_wayne"])
|
||||||
class TestDefaultGuest(EvenniaTest):
|
class TestDefaultGuest(EvenniaTest):
|
||||||
"Check DefaultGuest class"
|
"Check DefaultGuest class"
|
||||||
|
|
||||||
ip = '212.216.134.22'
|
ip = '212.216.134.22'
|
||||||
|
|
||||||
def test_authenticate(self):
|
@override_settings(GUEST_ENABLED=False)
|
||||||
|
def test_create_not_enabled(self):
|
||||||
# Guest account should not be permitted
|
# Guest account should not be permitted
|
||||||
account, errors = DefaultGuest.authenticate(ip=self.ip)
|
account, errors = DefaultGuest.authenticate(ip=self.ip)
|
||||||
self.assertFalse(account, 'Guest account was created despite being disabled.')
|
self.assertFalse(account, 'Guest account was created despite being disabled.')
|
||||||
|
|
||||||
settings.GUEST_ENABLED = True
|
def test_authenticate(self):
|
||||||
settings.GUEST_LIST = ['bruce_wayne']
|
|
||||||
|
|
||||||
# Create a guest account
|
# Create a guest account
|
||||||
account, errors = DefaultGuest.authenticate(ip=self.ip)
|
account, errors = DefaultGuest.authenticate(ip=self.ip)
|
||||||
self.assertTrue(account, 'Guest account should have been created.')
|
self.assertTrue(account, 'Guest account should have been created.')
|
||||||
|
|
@ -81,7 +83,32 @@ class TestDefaultGuest(EvenniaTest):
|
||||||
account, errors = DefaultGuest.authenticate(ip=self.ip)
|
account, errors = DefaultGuest.authenticate(ip=self.ip)
|
||||||
self.assertFalse(account, 'Two guest accounts were created with a single entry on the guest list!')
|
self.assertFalse(account, 'Two guest accounts were created with a single entry on the guest list!')
|
||||||
|
|
||||||
settings.GUEST_ENABLED = False
|
@patch("evennia.accounts.accounts.ChannelDB.objects.get_channel")
|
||||||
|
def test_create(self, get_channel):
|
||||||
|
get_channel.connect = MagicMock(return_value=True)
|
||||||
|
account, errors = DefaultGuest.create()
|
||||||
|
self.assertTrue(account, "Guest account should have been created.")
|
||||||
|
self.assertFalse(errors)
|
||||||
|
|
||||||
|
def test_at_post_login(self):
|
||||||
|
self.account.db._last_puppet = self.char1
|
||||||
|
self.account.at_post_login(self.session)
|
||||||
|
self.account.at_post_login()
|
||||||
|
|
||||||
|
def test_at_server_shutdown(self):
|
||||||
|
account, errors = DefaultGuest.create(ip=self.ip)
|
||||||
|
self.char1.delete = MagicMock()
|
||||||
|
account.db._playable_characters = [self.char1]
|
||||||
|
account.at_server_shutdown()
|
||||||
|
self.char1.delete.assert_called()
|
||||||
|
|
||||||
|
def test_at_post_disconnect(self):
|
||||||
|
account, errors = DefaultGuest.create(ip=self.ip)
|
||||||
|
self.char1.delete = MagicMock()
|
||||||
|
account.db._playable_characters = [self.char1]
|
||||||
|
account.at_post_disconnect()
|
||||||
|
self.char1.delete.assert_called()
|
||||||
|
|
||||||
|
|
||||||
class TestDefaultAccountAuth(EvenniaTest):
|
class TestDefaultAccountAuth(EvenniaTest):
|
||||||
|
|
||||||
|
|
@ -162,6 +189,7 @@ class TestDefaultAccountAuth(EvenniaTest):
|
||||||
self.assertFalse(account.set_password('Mxyzptlk'))
|
self.assertFalse(account.set_password('Mxyzptlk'))
|
||||||
account.delete()
|
account.delete()
|
||||||
|
|
||||||
|
|
||||||
class TestDefaultAccount(TestCase):
|
class TestDefaultAccount(TestCase):
|
||||||
"Check DefaultAccount class"
|
"Check DefaultAccount class"
|
||||||
|
|
||||||
|
|
@ -279,13 +307,83 @@ class TestAccountPuppetDeletion(EvenniaTest):
|
||||||
@override_settings(MULTISESSION_MODE=2)
|
@override_settings(MULTISESSION_MODE=2)
|
||||||
def test_puppet_deletion(self):
|
def test_puppet_deletion(self):
|
||||||
# Check for existing chars
|
# Check for existing chars
|
||||||
self.assertFalse(self.account.db._playable_characters, 'Account should not have any chars by default.')
|
self.assertFalse(self.account.db._playable_characters,
|
||||||
|
'Account should not have any chars by default.')
|
||||||
|
|
||||||
# Add char1 to account's playable characters
|
# Add char1 to account's playable characters
|
||||||
self.account.db._playable_characters.append(self.char1)
|
self.account.db._playable_characters.append(self.char1)
|
||||||
self.assertTrue(self.account.db._playable_characters, 'Char was not added to account.')
|
self.assertTrue(self.account.db._playable_characters,
|
||||||
|
'Char was not added to account.')
|
||||||
|
|
||||||
# See what happens when we delete char1.
|
# See what happens when we delete char1.
|
||||||
self.char1.delete()
|
self.char1.delete()
|
||||||
# Playable char list should be empty.
|
# Playable char list should be empty.
|
||||||
self.assertFalse(self.account.db._playable_characters, 'Playable character list is not empty! %s' % self.account.db._playable_characters)
|
self.assertFalse(self.account.db._playable_characters,
|
||||||
|
'Playable character list is not empty! %s' % self.account.db._playable_characters)
|
||||||
|
|
||||||
|
|
||||||
|
class TestDefaultAccountEv(EvenniaTest):
|
||||||
|
"""
|
||||||
|
Testing using the EvenniaTest parent
|
||||||
|
|
||||||
|
"""
|
||||||
|
def test_characters_property(self):
|
||||||
|
"test existence of None in _playable_characters Attr"
|
||||||
|
self.account.db._playable_characters = [self.char1, None]
|
||||||
|
chars = self.account.characters
|
||||||
|
self.assertEqual(chars, [self.char1])
|
||||||
|
self.assertEqual(self.account.db._playable_characters, [self.char1])
|
||||||
|
|
||||||
|
def test_puppet_success(self):
|
||||||
|
self.account.msg = MagicMock()
|
||||||
|
with patch("evennia.accounts.accounts._MULTISESSION_MODE", 2):
|
||||||
|
self.account.puppet_object(self.session, self.char1)
|
||||||
|
self.account.msg.assert_called_with("You are already puppeting this object.")
|
||||||
|
|
||||||
|
@patch("evennia.accounts.accounts.time.time", return_value=10000)
|
||||||
|
def test_idle_time(self, mock_time):
|
||||||
|
self.session.cmd_last_visible = 10000 - 10
|
||||||
|
idle = self.account.idle_time
|
||||||
|
self.assertEqual(idle, 10)
|
||||||
|
|
||||||
|
# test no sessions
|
||||||
|
with patch("evennia.accounts.accounts._SESSIONS.sessions_from_account", return_value=[]) as mock_sessh:
|
||||||
|
idle = self.account.idle_time
|
||||||
|
self.assertEqual(idle, None)
|
||||||
|
|
||||||
|
@patch("evennia.accounts.accounts.time.time", return_value=10000)
|
||||||
|
def test_connection_time(self, mock_time):
|
||||||
|
self.session.conn_time = 10000 - 10
|
||||||
|
conn = self.account.connection_time
|
||||||
|
self.assertEqual(conn, 10)
|
||||||
|
|
||||||
|
# test no sessions
|
||||||
|
with patch("evennia.accounts.accounts._SESSIONS.sessions_from_account", return_value=[]) as mock_sessh:
|
||||||
|
idle = self.account.connection_time
|
||||||
|
self.assertEqual(idle, None)
|
||||||
|
|
||||||
|
def test_create_account(self):
|
||||||
|
acct = create.account(
|
||||||
|
"TestAccount3", "test@test.com", "testpassword123",
|
||||||
|
locks="test:all()",
|
||||||
|
tags=[("tag1", "category1"), ("tag2", "category2", "data1"), ("tag3", None)],
|
||||||
|
attributes=[("key1", "value1", "category1",
|
||||||
|
"edit:false()", True),
|
||||||
|
("key2", "value2")])
|
||||||
|
acct.save()
|
||||||
|
self.assertTrue(acct.pk)
|
||||||
|
|
||||||
|
def test_at_look(self):
|
||||||
|
ret = self.account.at_look()
|
||||||
|
self.assertTrue("Out-of-Character" in ret)
|
||||||
|
ret = self.account.at_look(target=self.obj1)
|
||||||
|
self.assertTrue("Obj" in ret)
|
||||||
|
ret = self.account.at_look(session=self.session)
|
||||||
|
self.assertTrue("*" in ret) # * marks session is active in list
|
||||||
|
ret = self.account.at_look(target=self.obj1, session=self.session)
|
||||||
|
self.assertTrue("Obj" in ret)
|
||||||
|
ret = self.account.at_look(target="Invalid", session=self.session)
|
||||||
|
self.assertEqual(ret, 'Invalid has no in-game appearance.')
|
||||||
|
|
||||||
|
def test_msg(self):
|
||||||
|
self.account.msg
|
||||||
|
|
|
||||||
|
|
@ -1040,6 +1040,11 @@ class CmdLink(COMMAND_DEFAULT_CLASS):
|
||||||
if not target:
|
if not target:
|
||||||
return
|
return
|
||||||
|
|
||||||
|
if target == obj:
|
||||||
|
self.caller.msg("Cannot link an object to itself.")
|
||||||
|
return
|
||||||
|
|
||||||
|
|
||||||
string = ""
|
string = ""
|
||||||
note = "Note: %s(%s) did not have a destination set before. Make sure you linked the right thing."
|
note = "Note: %s(%s) did not have a destination set before. Make sure you linked the right thing."
|
||||||
if not obj.destination:
|
if not obj.destination:
|
||||||
|
|
@ -1123,6 +1128,7 @@ class CmdSetHome(CmdLink):
|
||||||
|
|
||||||
Usage:
|
Usage:
|
||||||
@sethome <obj> [= <home_location>]
|
@sethome <obj> [= <home_location>]
|
||||||
|
@sethom <obj>
|
||||||
|
|
||||||
The "home" location is a "safety" location for objects; they
|
The "home" location is a "safety" location for objects; they
|
||||||
will be moved there if their current location ceases to exist. All
|
will be moved there if their current location ceases to exist. All
|
||||||
|
|
@ -1162,10 +1168,10 @@ class CmdSetHome(CmdLink):
|
||||||
old_home = obj.home
|
old_home = obj.home
|
||||||
obj.home = new_home
|
obj.home = new_home
|
||||||
if old_home:
|
if old_home:
|
||||||
string = "%s's home location was changed from %s(%s) to %s(%s)." % (
|
string = "Home location of %s was changed from %s(%s) to %s(%s)." % (
|
||||||
obj, old_home, old_home.dbref, new_home, new_home.dbref)
|
obj, old_home, old_home.dbref, new_home, new_home.dbref)
|
||||||
else:
|
else:
|
||||||
string = "%s' home location was set to %s(%s)." % (obj, new_home, new_home.dbref)
|
string = "Home location of %s was set to %s(%s)." % (obj, new_home, new_home.dbref)
|
||||||
self.caller.msg(string)
|
self.caller.msg(string)
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -1430,37 +1436,6 @@ def _convert_from_string(cmd, strobj):
|
||||||
string this will always fail).
|
string this will always fail).
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def rec_convert(obj):
|
|
||||||
"""
|
|
||||||
Helper function of recursive conversion calls. This is only
|
|
||||||
used for Python <=2.5. After that literal_eval is available.
|
|
||||||
"""
|
|
||||||
# simple types
|
|
||||||
try:
|
|
||||||
return int(obj)
|
|
||||||
except ValueError:
|
|
||||||
# obj cannot be converted to int - that's fine
|
|
||||||
pass
|
|
||||||
try:
|
|
||||||
return float(obj)
|
|
||||||
except ValueError:
|
|
||||||
# obj cannot be converted to float - that's fine
|
|
||||||
pass
|
|
||||||
# iterables
|
|
||||||
if obj.startswith('[') and obj.endswith(']'):
|
|
||||||
"A list. Traverse recursively."
|
|
||||||
return [rec_convert(val) for val in obj[1:-1].split(',')]
|
|
||||||
if obj.startswith('(') and obj.endswith(')'):
|
|
||||||
"A tuple. Traverse recursively."
|
|
||||||
return tuple([rec_convert(val) for val in obj[1:-1].split(',')])
|
|
||||||
if obj.startswith('{') and obj.endswith('}') and ':' in obj:
|
|
||||||
"A dict. Traverse recursively."
|
|
||||||
return dict([(rec_convert(pair.split(":", 1)[0]),
|
|
||||||
rec_convert(pair.split(":", 1)[1]))
|
|
||||||
for pair in obj[1:-1].split(',') if ":" in pair])
|
|
||||||
# if nothing matches, return as-is
|
|
||||||
return obj
|
|
||||||
|
|
||||||
# Use literal_eval to parse python structure exactly.
|
# Use literal_eval to parse python structure exactly.
|
||||||
try:
|
try:
|
||||||
return _LITERAL_EVAL(strobj)
|
return _LITERAL_EVAL(strobj)
|
||||||
|
|
@ -1471,10 +1446,9 @@ def _convert_from_string(cmd, strobj):
|
||||||
"Make sure this is acceptable." % strobj
|
"Make sure this is acceptable." % strobj
|
||||||
cmd.caller.msg(string)
|
cmd.caller.msg(string)
|
||||||
return strobj
|
return strobj
|
||||||
else:
|
except Exception as err:
|
||||||
# fall back to old recursive solution (does not support
|
string = "|RUnknown error in evaluating Attribute: {}".format(err)
|
||||||
# nested lists/dicts)
|
return string
|
||||||
return rec_convert(strobj.strip())
|
|
||||||
|
|
||||||
|
|
||||||
class CmdSetAttribute(ObjManipCommand):
|
class CmdSetAttribute(ObjManipCommand):
|
||||||
|
|
@ -1957,7 +1931,7 @@ class CmdLock(ObjManipCommand):
|
||||||
|
|
||||||
caller = self.caller
|
caller = self.caller
|
||||||
if not self.args:
|
if not self.args:
|
||||||
string = "@lock <object>[ = <lockstring>] or @lock[/switch] " \
|
string = "Usage: @lock <object>[ = <lockstring>] or @lock[/switch] " \
|
||||||
"<object>/<access_type>"
|
"<object>/<access_type>"
|
||||||
caller.msg(string)
|
caller.msg(string)
|
||||||
return
|
return
|
||||||
|
|
@ -1978,8 +1952,8 @@ class CmdLock(ObjManipCommand):
|
||||||
caller.msg("You need 'control' access to change this type of lock.")
|
caller.msg("You need 'control' access to change this type of lock.")
|
||||||
return
|
return
|
||||||
|
|
||||||
if not has_control_access or obj.access(caller, "edit"):
|
if not has_control_access or not obj.access(caller, "edit"):
|
||||||
caller.msg("You are not allowed to do that.")
|
caller.msg("You need 'edit' access to view or delete lock on this object.")
|
||||||
return
|
return
|
||||||
|
|
||||||
lockdef = obj.locks.get(access_type)
|
lockdef = obj.locks.get(access_type)
|
||||||
|
|
@ -2753,11 +2727,11 @@ class CmdTag(COMMAND_DEFAULT_CLASS):
|
||||||
obj)
|
obj)
|
||||||
else:
|
else:
|
||||||
# no tag specified, clear all tags
|
# no tag specified, clear all tags
|
||||||
old_tags = ["%s%s" % (tag, " (category: %s" % category if category else "")
|
old_tags = ["%s%s" % (tag, " (category: %s)" % category if category else "")
|
||||||
for tag, category in obj.tags.all(return_key_and_category=True)]
|
for tag, category in obj.tags.all(return_key_and_category=True)]
|
||||||
if old_tags:
|
if old_tags:
|
||||||
obj.tags.clear()
|
obj.tags.clear()
|
||||||
string = "Cleared all tags from %s: %s" % (obj, ", ".join(old_tags))
|
string = "Cleared all tags from %s: %s" % (obj, ", ".join(sorted(old_tags)))
|
||||||
else:
|
else:
|
||||||
string = "No Tags to clear on %s." % obj
|
string = "No Tags to clear on %s." % obj
|
||||||
self.caller.msg(string)
|
self.caller.msg(string)
|
||||||
|
|
@ -2788,8 +2762,9 @@ class CmdTag(COMMAND_DEFAULT_CLASS):
|
||||||
tags = [tup[0] for tup in tagtuples]
|
tags = [tup[0] for tup in tagtuples]
|
||||||
categories = [" (category: %s)" % tup[1] if tup[1] else "" for tup in tagtuples]
|
categories = [" (category: %s)" % tup[1] if tup[1] else "" for tup in tagtuples]
|
||||||
if ntags:
|
if ntags:
|
||||||
string = "Tag%s on %s: %s" % ("s" if ntags > 1 else "", obj,
|
string = "Tag%s on %s: %s" % (
|
||||||
", ".join("'%s'%s" % (tags[i], categories[i]) for i in range(ntags)))
|
"s" if ntags > 1 else "", obj,
|
||||||
|
", ".join(sorted("'%s'%s" % (tags[i], categories[i]) for i in range(ntags))))
|
||||||
else:
|
else:
|
||||||
string = "No tags attached to %s." % obj
|
string = "No tags attached to %s." % obj
|
||||||
self.caller.msg(string)
|
self.caller.msg(string)
|
||||||
|
|
@ -3002,7 +2977,7 @@ class CmdSpawn(COMMAND_DEFAULT_CLASS):
|
||||||
|
|
||||||
# all seems ok. Try to save.
|
# all seems ok. Try to save.
|
||||||
try:
|
try:
|
||||||
prot = protlib.save_prototype(**prototype)
|
prot = protlib.save_prototype(prototype)
|
||||||
if not prot:
|
if not prot:
|
||||||
caller.msg("|rError saving:|R {}.|n".format(prototype_key))
|
caller.msg("|rError saving:|R {}.|n".format(prototype_key))
|
||||||
return
|
return
|
||||||
|
|
@ -3091,7 +3066,7 @@ class CmdSpawn(COMMAND_DEFAULT_CLASS):
|
||||||
return
|
return
|
||||||
elif nprots > 1:
|
elif nprots > 1:
|
||||||
caller.msg("Found {} prototypes matching '{}':\n {}".format(
|
caller.msg("Found {} prototypes matching '{}':\n {}".format(
|
||||||
nprots, prototype, ", ".join(prot.get('prototype_key', '')
|
nprots, prototype, ", ".join(proto.get('prototype_key', '')
|
||||||
for proto in prototypes)))
|
for proto in prototypes)))
|
||||||
return
|
return
|
||||||
# we have a prototype, check access
|
# we have a prototype, check access
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,7 @@ System commands
|
||||||
|
|
||||||
import traceback
|
import traceback
|
||||||
import os
|
import os
|
||||||
|
import io
|
||||||
import datetime
|
import datetime
|
||||||
import sys
|
import sys
|
||||||
import django
|
import django
|
||||||
|
|
@ -21,7 +22,7 @@ from evennia.accounts.models import AccountDB
|
||||||
from evennia.utils import logger, utils, gametime, create, search
|
from evennia.utils import logger, utils, gametime, create, search
|
||||||
from evennia.utils.eveditor import EvEditor
|
from evennia.utils.eveditor import EvEditor
|
||||||
from evennia.utils.evtable import EvTable
|
from evennia.utils.evtable import EvTable
|
||||||
from evennia.utils.utils import crop, class_from_module
|
from evennia.utils.utils import crop, class_from_module, to_unicode
|
||||||
|
|
||||||
COMMAND_DEFAULT_CLASS = class_from_module(settings.COMMAND_DEFAULT_CLASS)
|
COMMAND_DEFAULT_CLASS = class_from_module(settings.COMMAND_DEFAULT_CLASS)
|
||||||
|
|
||||||
|
|
@ -197,6 +198,7 @@ def _run_code_snippet(caller, pycode, mode="eval", measure_time=False,
|
||||||
duration = " (runtime ~ %.4f ms)" % ((t1 - t0) * 1000)
|
duration = " (runtime ~ %.4f ms)" % ((t1 - t0) * 1000)
|
||||||
else:
|
else:
|
||||||
ret = eval(pycode_compiled, {}, available_vars)
|
ret = eval(pycode_compiled, {}, available_vars)
|
||||||
|
|
||||||
if mode == "eval":
|
if mode == "eval":
|
||||||
ret = "%s%s" % (str(ret), duration)
|
ret = "%s%s" % (str(ret), duration)
|
||||||
else:
|
else:
|
||||||
|
|
@ -238,7 +240,9 @@ class CmdPy(COMMAND_DEFAULT_CLASS):
|
||||||
inherits_from(obj, parent) : check object inheritance
|
inherits_from(obj, parent) : check object inheritance
|
||||||
|
|
||||||
You can explore The evennia API from inside the game by calling
|
You can explore The evennia API from inside the game by calling
|
||||||
evennia.help(), evennia.managers.help() etc.
|
the `__doc__` property on entities:
|
||||||
|
@py evennia.__doc__
|
||||||
|
@py evennia.managers.__doc__
|
||||||
|
|
||||||
|rNote: In the wrong hands this command is a severe security risk.
|
|rNote: In the wrong hands this command is a severe security risk.
|
||||||
It should only be accessible by trusted server admins/superusers.|n
|
It should only be accessible by trusted server admins/superusers.|n
|
||||||
|
|
|
||||||
|
|
@ -11,10 +11,10 @@ main test suite started with
|
||||||
> python game/manage.py test.
|
> python game/manage.py test.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import re
|
import re
|
||||||
import types
|
import types
|
||||||
import datetime
|
import datetime
|
||||||
|
from anything import Anything
|
||||||
|
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from mock import Mock, mock
|
from mock import Mock, mock
|
||||||
|
|
@ -49,7 +49,7 @@ class CommandTest(EvenniaTest):
|
||||||
Tests a command
|
Tests a command
|
||||||
"""
|
"""
|
||||||
def call(self, cmdobj, args, msg=None, cmdset=None, noansi=True, caller=None,
|
def call(self, cmdobj, args, msg=None, cmdset=None, noansi=True, caller=None,
|
||||||
receiver=None, cmdstring=None, obj=None, inputs=None):
|
receiver=None, cmdstring=None, obj=None, inputs=None, raw_string=None):
|
||||||
"""
|
"""
|
||||||
Test a command by assigning all the needed
|
Test a command by assigning all the needed
|
||||||
properties to cmdobj and running
|
properties to cmdobj and running
|
||||||
|
|
@ -74,7 +74,7 @@ class CommandTest(EvenniaTest):
|
||||||
cmdobj.cmdset = cmdset
|
cmdobj.cmdset = cmdset
|
||||||
cmdobj.session = SESSIONS.session_from_sessid(1)
|
cmdobj.session = SESSIONS.session_from_sessid(1)
|
||||||
cmdobj.account = self.account
|
cmdobj.account = self.account
|
||||||
cmdobj.raw_string = cmdobj.key + " " + args
|
cmdobj.raw_string = raw_string if raw_string is not None else cmdobj.key + " " + args
|
||||||
cmdobj.obj = obj or (caller if caller else self.char1)
|
cmdobj.obj = obj or (caller if caller else self.char1)
|
||||||
# test
|
# test
|
||||||
old_msg = receiver.msg
|
old_msg = receiver.msg
|
||||||
|
|
@ -322,30 +322,90 @@ class TestBuilding(CommandTest):
|
||||||
name = settings.BASE_OBJECT_TYPECLASS.rsplit('.', 1)[1]
|
name = settings.BASE_OBJECT_TYPECLASS.rsplit('.', 1)[1]
|
||||||
self.call(building.CmdCreate(), "/d TestObj1", # /d switch is abbreviated form of /drop
|
self.call(building.CmdCreate(), "/d TestObj1", # /d switch is abbreviated form of /drop
|
||||||
"You create a new %s: TestObj1." % name)
|
"You create a new %s: TestObj1." % name)
|
||||||
|
self.call(building.CmdCreate(), "", "Usage: ")
|
||||||
|
self.call(building.CmdCreate(), "TestObj1;foo;bar",
|
||||||
|
"You create a new %s: TestObj1 (aliases: foo, bar)." % name)
|
||||||
|
|
||||||
def test_examine(self):
|
def test_examine(self):
|
||||||
|
self.call(building.CmdExamine(), "", "Name/key: Room")
|
||||||
self.call(building.CmdExamine(), "Obj", "Name/key: Obj")
|
self.call(building.CmdExamine(), "Obj", "Name/key: Obj")
|
||||||
|
self.call(building.CmdExamine(), "Obj", "Name/key: Obj")
|
||||||
|
self.call(building.CmdExamine(), "*TestAccount", "Name/key: TestAccount")
|
||||||
|
|
||||||
|
self.char1.db.test = "testval"
|
||||||
|
self.call(building.CmdExamine(), "self/test", "Persistent attributes:\n test = testval")
|
||||||
|
self.call(building.CmdExamine(), "NotFound", "Could not find 'NotFound'.")
|
||||||
|
self.call(building.CmdExamine(), "out", "Name/key: out")
|
||||||
|
|
||||||
|
self.room1.scripts.add(self.script.__class__)
|
||||||
|
self.call(building.CmdExamine(), "")
|
||||||
|
self.account.scripts.add(self.script.__class__)
|
||||||
|
self.call(building.CmdExamine(), "*TestAccount")
|
||||||
|
|
||||||
def test_set_obj_alias(self):
|
def test_set_obj_alias(self):
|
||||||
self.call(building.CmdSetObjAlias(), "Obj =", "Cleared aliases from Obj(#4)")
|
self.call(building.CmdSetObjAlias(), "Obj =", "Cleared aliases from Obj")
|
||||||
self.call(building.CmdSetObjAlias(), "Obj = TestObj1b", "Alias(es) for 'Obj(#4)' set to 'testobj1b'.")
|
self.call(building.CmdSetObjAlias(), "Obj = TestObj1b", "Alias(es) for 'Obj(#4)' set to 'testobj1b'.")
|
||||||
|
self.call(building.CmdSetObjAlias(), "", "Usage: ")
|
||||||
|
self.call(building.CmdSetObjAlias(), "NotFound =", "Could not find 'NotFound'.")
|
||||||
|
self.call(building.CmdSetObjAlias(), "Obj", "Aliases for Obj(#4): 'testobj1b'")
|
||||||
|
self.call(building.CmdSetObjAlias(), "Obj2 =", "Cleared aliases from Obj2")
|
||||||
|
self.call(building.CmdSetObjAlias(), "Obj2 =", "No aliases to clear.")
|
||||||
|
|
||||||
def test_copy(self):
|
def test_copy(self):
|
||||||
self.call(building.CmdCopy(), "Obj = TestObj2;TestObj2b, TestObj3;TestObj3b",
|
self.call(building.CmdCopy(), "Obj = TestObj2;TestObj2b, TestObj3;TestObj3b",
|
||||||
"Copied Obj to 'TestObj3' (aliases: ['TestObj3b']")
|
"Copied Obj to 'TestObj3' (aliases: ['TestObj3b']")
|
||||||
|
self.call(building.CmdCopy(), "", "Usage: ")
|
||||||
|
self.call(building.CmdCopy(), "Obj", "Identical copy of Obj, named 'Obj_copy' was created.")
|
||||||
|
self.call(building.CmdCopy(), "NotFound = Foo", "Could not find 'NotFound'.")
|
||||||
|
|
||||||
def test_attribute_commands(self):
|
def test_attribute_commands(self):
|
||||||
|
self.call(building.CmdSetAttribute(), "", "Usage: ")
|
||||||
self.call(building.CmdSetAttribute(), "Obj/test1=\"value1\"", "Created attribute Obj/test1 = 'value1'")
|
self.call(building.CmdSetAttribute(), "Obj/test1=\"value1\"", "Created attribute Obj/test1 = 'value1'")
|
||||||
self.call(building.CmdSetAttribute(), "Obj2/test2=\"value2\"", "Created attribute Obj2/test2 = 'value2'")
|
self.call(building.CmdSetAttribute(), "Obj2/test2=\"value2\"", "Created attribute Obj2/test2 = 'value2'")
|
||||||
|
self.call(building.CmdSetAttribute(), "Obj2/test2", "Attribute Obj2/test2 = value2")
|
||||||
|
self.call(building.CmdSetAttribute(), "Obj2/NotFound", "Obj2 has no attribute 'notfound'.")
|
||||||
|
|
||||||
|
with mock.patch("evennia.commands.default.building.EvEditor") as mock_ed:
|
||||||
|
self.call(building.CmdSetAttribute(), "/edit Obj2/test3")
|
||||||
|
mock_ed.assert_called_with(self.char1, Anything, Anything, key='Obj2/test3')
|
||||||
|
|
||||||
|
self.call(building.CmdSetAttribute(), "Obj2/test3=\"value3\"", "Created attribute Obj2/test3 = 'value3'")
|
||||||
|
self.call(building.CmdSetAttribute(), "Obj2/test3 = ", "Deleted attribute 'test3' (= True) from Obj2.")
|
||||||
|
|
||||||
|
self.call(building.CmdCpAttr(), "/copy Obj2/test2 = Obj2/test3",
|
||||||
|
"@cpattr: Extra switch \"/copy\" ignored.|\nCopied Obj2.test2 -> Obj2.test3. "
|
||||||
|
"(value: 'value2')")
|
||||||
|
self.call(building.CmdMvAttr(), "", "Usage: ")
|
||||||
self.call(building.CmdMvAttr(), "Obj2/test2 = Obj/test3", "Moved Obj2.test2 -> Obj.test3")
|
self.call(building.CmdMvAttr(), "Obj2/test2 = Obj/test3", "Moved Obj2.test2 -> Obj.test3")
|
||||||
|
self.call(building.CmdCpAttr(), "", "Usage: ")
|
||||||
self.call(building.CmdCpAttr(), "Obj/test1 = Obj2/test3", "Copied Obj.test1 -> Obj2.test3")
|
self.call(building.CmdCpAttr(), "Obj/test1 = Obj2/test3", "Copied Obj.test1 -> Obj2.test3")
|
||||||
|
|
||||||
|
self.call(building.CmdWipe(), "", "Usage: ")
|
||||||
self.call(building.CmdWipe(), "Obj2/test2/test3", "Wiped attributes test2,test3 on Obj2.")
|
self.call(building.CmdWipe(), "Obj2/test2/test3", "Wiped attributes test2,test3 on Obj2.")
|
||||||
|
self.call(building.CmdWipe(), "Obj2", "Wiped all attributes on Obj2.")
|
||||||
|
|
||||||
def test_name(self):
|
def test_name(self):
|
||||||
|
self.call(building.CmdName(), "", "Usage: ")
|
||||||
self.call(building.CmdName(), "Obj2=Obj3", "Object's name changed to 'Obj3'.")
|
self.call(building.CmdName(), "Obj2=Obj3", "Object's name changed to 'Obj3'.")
|
||||||
|
self.call(building.CmdName(), "*TestAccount=TestAccountRenamed",
|
||||||
|
"Account's name changed to 'TestAccountRenamed'.")
|
||||||
|
self.call(building.CmdName(), "*NotFound=TestAccountRenamed",
|
||||||
|
"Could not find '*NotFound'")
|
||||||
|
self.call(building.CmdName(), "Obj3=Obj4;foo;bar",
|
||||||
|
"Object's name changed to 'Obj4' (foo, bar).")
|
||||||
|
self.call(building.CmdName(), "Obj4=", "No names or aliases defined!")
|
||||||
|
|
||||||
def test_desc(self):
|
def test_desc(self):
|
||||||
self.call(building.CmdDesc(), "Obj2=TestDesc", "The description was set on Obj2(#5).")
|
self.call(building.CmdDesc(), "Obj2=TestDesc", "The description was set on Obj2(#5).")
|
||||||
|
self.call(building.CmdDesc(), "", "Usage: ")
|
||||||
|
|
||||||
|
with mock.patch("evennia.commands.default.building.EvEditor") as mock_ed:
|
||||||
|
self.call(building.CmdDesc(), "/edit")
|
||||||
|
mock_ed.assert_called_with(self.char1, key='desc',
|
||||||
|
loadfunc=building._desc_load,
|
||||||
|
quitfunc=building._desc_quit,
|
||||||
|
savefunc=building._desc_save,
|
||||||
|
persistent=True)
|
||||||
|
|
||||||
def test_empty_desc(self):
|
def test_empty_desc(self):
|
||||||
"""
|
"""
|
||||||
|
|
@ -365,50 +425,128 @@ class TestBuilding(CommandTest):
|
||||||
assert self.obj2.db.desc == o2d
|
assert self.obj2.db.desc == o2d
|
||||||
assert self.room1.db.desc == 'Obj2' and self.room1.db.desc != r1d
|
assert self.room1.db.desc == 'Obj2' and self.room1.db.desc != r1d
|
||||||
|
|
||||||
def test_wipe(self):
|
def test_destroy(self):
|
||||||
confirm = building.CmdDestroy.confirm
|
confirm = building.CmdDestroy.confirm
|
||||||
building.CmdDestroy.confirm = False
|
building.CmdDestroy.confirm = False
|
||||||
|
self.call(building.CmdDestroy(), "", "Usage: ")
|
||||||
self.call(building.CmdDestroy(), "Obj", "Obj was destroyed.")
|
self.call(building.CmdDestroy(), "Obj", "Obj was destroyed.")
|
||||||
|
self.call(building.CmdDestroy(), "Obj", "Obj2 was destroyed.")
|
||||||
|
self.call(building.CmdDestroy(), "Obj", "Could not find 'Obj'.| (Objects to destroy "
|
||||||
|
"must either be local or specified with a unique #dbref.)")
|
||||||
|
self.call(building.CmdDestroy(), "#1", "You are trying to delete") # DEFAULT_HOME
|
||||||
|
self.char2.location = self.room2
|
||||||
|
self.call(building.CmdDestroy(), self.room2.dbref,
|
||||||
|
"Char2(#7) arrives to Room(#1) from Room2(#2).|Room2 was destroyed.")
|
||||||
building.CmdDestroy.confirm = confirm
|
building.CmdDestroy.confirm = confirm
|
||||||
|
|
||||||
|
def test_destroy_sequence(self):
|
||||||
|
confirm = building.CmdDestroy.confirm
|
||||||
|
building.CmdDestroy.confirm = False
|
||||||
|
self.call(building.CmdDestroy(),
|
||||||
|
"{}-{}".format(self.obj1.dbref, self.obj2.dbref),
|
||||||
|
"Obj was destroyed.\nObj2 was destroyed.")
|
||||||
|
|
||||||
def test_dig(self):
|
def test_dig(self):
|
||||||
self.call(building.CmdDig(), "TestRoom1=testroom;tr,back;b", "Created room TestRoom1")
|
self.call(building.CmdDig(), "TestRoom1=testroom;tr,back;b", "Created room TestRoom1")
|
||||||
|
self.call(building.CmdDig(), "", "Usage: ")
|
||||||
|
|
||||||
def test_tunnel(self):
|
def test_tunnel(self):
|
||||||
self.call(building.CmdTunnel(), "n = TestRoom2;test2", "Created room TestRoom2")
|
self.call(building.CmdTunnel(), "n = TestRoom2;test2", "Created room TestRoom2")
|
||||||
|
self.call(building.CmdTunnel(), "", "Usage: ")
|
||||||
|
self.call(building.CmdTunnel(), "foo = TestRoom2;test2", "@tunnel can only understand the")
|
||||||
|
self.call(building.CmdTunnel(), "/tel e = TestRoom3;test3",
|
||||||
|
"Created room TestRoom3(#11) (test3) of type typeclasses.rooms.Room.\n"
|
||||||
|
"Created Exit from Room to TestRoom3: east(#12) (e).\n"
|
||||||
|
"Created Exit back from TestRoom3 to Room: west(#13) (w).|TestRoom3(#11)")
|
||||||
|
|
||||||
def test_tunnel_exit_typeclass(self):
|
def test_tunnel_exit_typeclass(self):
|
||||||
self.call(building.CmdTunnel(), "n:evennia.objects.objects.DefaultExit = TestRoom3", "Created room TestRoom3")
|
self.call(building.CmdTunnel(), "n:evennia.objects.objects.DefaultExit = TestRoom3",
|
||||||
|
"Created room TestRoom3")
|
||||||
|
|
||||||
def test_exit_commands(self):
|
def test_exit_commands(self):
|
||||||
self.call(building.CmdOpen(), "TestExit1=Room2", "Created new Exit 'TestExit1' from Room to Room2")
|
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.CmdLink(), "TestExit1=Room", "Link created TestExit1 -> Room (one way).")
|
||||||
|
self.call(building.CmdUnLink(), "", "Usage: ")
|
||||||
|
self.call(building.CmdLink(), "NotFound", "Could not find 'NotFound'.")
|
||||||
|
self.call(building.CmdLink(), "TestExit", "TestExit1 is an exit to Room.")
|
||||||
|
self.call(building.CmdLink(), "Obj", "Obj is not an exit. Its home location is Room.")
|
||||||
self.call(building.CmdUnLink(), "TestExit1", "Former exit TestExit1 no longer links anywhere.")
|
self.call(building.CmdUnLink(), "TestExit1", "Former exit TestExit1 no longer links anywhere.")
|
||||||
|
|
||||||
self.char1.location = self.room2
|
self.char1.location = self.room2
|
||||||
self.call(building.CmdOpen(), "TestExit2=Room", "Created new Exit 'TestExit2' from Room2 to Room.")
|
self.call(building.CmdOpen(), "TestExit2=Room", "Created new Exit 'TestExit2' from Room2 to Room.")
|
||||||
|
self.call(building.CmdOpen(), "TestExit2=Room", "Exit TestExit2 already exists. It already points to the correct place.")
|
||||||
|
|
||||||
|
|
||||||
# ensure it matches locally first
|
# ensure it matches locally first
|
||||||
self.call(building.CmdLink(), "TestExit=Room2", "Link created TestExit2 -> Room2 (one way).")
|
self.call(building.CmdLink(), "TestExit=Room2", "Link created TestExit2 -> Room2 (one way).")
|
||||||
|
self.call(building.CmdLink(), "/twoway TestExit={}".format(self.exit.dbref),
|
||||||
|
"Link created TestExit2 (in Room2) <-> out (in Room) (two-way).")
|
||||||
|
self.call(building.CmdLink(), "/twoway TestExit={}".format(self.room1.dbref),
|
||||||
|
"To create a two-way link, TestExit2 and Room must both have a location ")
|
||||||
|
self.call(building.CmdLink(), "/twoway {}={}".format(self.exit.dbref, self.exit.dbref),
|
||||||
|
"Cannot link an object to itself.")
|
||||||
|
self.call(building.CmdLink(), "", "Usage: ")
|
||||||
# ensure can still match globally when not a local name
|
# 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. "
|
self.call(building.CmdLink(), "TestExit1=Room2", "Note: TestExit1(#8) did not have a destination set before. "
|
||||||
"Make sure you linked the right thing.\n"
|
"Make sure you linked the right thing.\n"
|
||||||
"Link created TestExit1 -> Room2 (one way).")
|
"Link created TestExit1 -> Room2 (one way).")
|
||||||
|
self.call(building.CmdLink(), "TestExit1=", "Former exit TestExit1 no longer links anywhere.")
|
||||||
|
|
||||||
def test_set_home(self):
|
def test_set_home(self):
|
||||||
self.call(building.CmdSetHome(), "Obj = Room2", "Obj's home location was changed from Room")
|
self.call(building.CmdSetHome(), "Obj = Room2", "Home location of Obj was changed from Room")
|
||||||
|
self.call(building.CmdSetHome(), "", "Usage: ")
|
||||||
|
self.call(building.CmdSetHome(), "self", "Char's current home is Room")
|
||||||
|
self.call(building.CmdSetHome(), "Obj", "Obj's current home is Room2")
|
||||||
|
self.obj1.home = None
|
||||||
|
self.call(building.CmdSetHome(), "Obj = Room2", "Home location of Obj was set to Room")
|
||||||
|
|
||||||
def test_list_cmdsets(self):
|
def test_list_cmdsets(self):
|
||||||
self.call(building.CmdListCmdSets(), "", "<DefaultCharacter (Union, prio 0, perm)>:")
|
self.call(building.CmdListCmdSets(), "", "<DefaultCharacter (Union, prio 0, perm)>:")
|
||||||
|
self.call(building.CmdListCmdSets(), "NotFound", "Could not find 'NotFound'")
|
||||||
|
|
||||||
def test_typeclass(self):
|
def test_typeclass(self):
|
||||||
|
self.call(building.CmdTypeclass(), "", "Usage: ")
|
||||||
self.call(building.CmdTypeclass(), "Obj = evennia.objects.objects.DefaultExit",
|
self.call(building.CmdTypeclass(), "Obj = evennia.objects.objects.DefaultExit",
|
||||||
"Obj changed typeclass from evennia.objects.objects.DefaultObject "
|
"Obj changed typeclass from evennia.objects.objects.DefaultObject "
|
||||||
"to evennia.objects.objects.DefaultExit.")
|
"to evennia.objects.objects.DefaultExit.")
|
||||||
|
self.call(building.CmdTypeclass(), "Obj2 = evennia.objects.objects.DefaultExit",
|
||||||
|
"Obj2 changed typeclass from evennia.objects.objects.DefaultObject "
|
||||||
|
"to evennia.objects.objects.DefaultExit.", cmdstring="@swap")
|
||||||
|
self.call(building.CmdTypeclass(), "/list Obj", "Core typeclasses")
|
||||||
|
self.call(building.CmdTypeclass(), "/show Obj", "Obj's current typeclass is 'evennia.objects.objects.DefaultExit'")
|
||||||
|
self.call(building.CmdTypeclass(), "Obj = evennia.objects.objects.DefaultExit",
|
||||||
|
"Obj already has the typeclass 'evennia.objects.objects.DefaultExit'. Use /force to override.")
|
||||||
|
self.call(building.CmdTypeclass(), "/force Obj = evennia.objects.objects.DefaultExit",
|
||||||
|
"Obj updated its existing typeclass ")
|
||||||
|
self.call(building.CmdTypeclass(), "Obj = evennia.objects.objects.DefaultObject")
|
||||||
|
self.call(building.CmdTypeclass(), "/show Obj", "Obj's current typeclass is 'evennia.objects.objects.DefaultObject'")
|
||||||
|
self.call(building.CmdTypeclass(), "Obj",
|
||||||
|
"Obj updated its existing typeclass (evennia.objects.objects.DefaultObject).\n"
|
||||||
|
"Only the at_object_creation hook was run (update mode). Attributes set before swap were not removed.",
|
||||||
|
cmdstring="@update")
|
||||||
|
self.call(building.CmdTypeclass(), "/reset/force Obj=evennia.objects.objects.DefaultObject",
|
||||||
|
"Obj updated its existing typeclass (evennia.objects.objects.DefaultObject).\n"
|
||||||
|
"All object creation hooks were run. All old attributes where deleted before the swap.")
|
||||||
|
|
||||||
def test_lock(self):
|
def test_lock(self):
|
||||||
self.call(building.CmdLock(), "Obj = test:perm(Developer)", "Added lock 'test:perm(Developer)' to Obj.")
|
self.call(building.CmdLock(), "", "Usage: ")
|
||||||
|
self.call(building.CmdLock(), "Obj = test:all()", "Added lock 'test:all()' to Obj.")
|
||||||
|
self.call(building.CmdLock(), "*TestAccount = test:all()", "Added lock 'test:all()' to TestAccount")
|
||||||
|
self.call(building.CmdLock(), "Obj/notfound", "Obj has no lock of access type 'notfound'.")
|
||||||
|
self.call(building.CmdLock(), "Obj/test", "test:all()")
|
||||||
|
self.call(building.CmdLock(), "/view Obj = edit:false()",
|
||||||
|
"Switch(es) view can not be used with a lock assignment. "
|
||||||
|
"Use e.g. @lock/del objname/locktype instead.")
|
||||||
|
self.call(building.CmdLock(), "Obj = edit:false()")
|
||||||
|
self.call(building.CmdLock(), "Obj/test", "You need 'edit' access to view or delete lock on this object.")
|
||||||
|
self.call(building.CmdLock(), "Obj", "call:true()") # etc
|
||||||
|
self.call(building.CmdLock(), "*TestAccount", "boot:perm(Admin)") # etc
|
||||||
|
|
||||||
def test_find(self):
|
def test_find(self):
|
||||||
|
self.call(building.CmdFind(), "", "Usage: ")
|
||||||
self.call(building.CmdFind(), "oom2", "One Match")
|
self.call(building.CmdFind(), "oom2", "One Match")
|
||||||
|
self.call(building.CmdFind(), "oom2 = 1-100", "One Match")
|
||||||
|
self.call(building.CmdFind(), "oom2 = 1 100", "One Match") # space works too
|
||||||
expect = "One Match(#1-#7, loc):\n " +\
|
expect = "One Match(#1-#7, loc):\n " +\
|
||||||
"Char2(#7) - evennia.objects.objects.DefaultCharacter (location: Room(#1))"
|
"Char2(#7) - evennia.objects.objects.DefaultCharacter (location: Room(#1))"
|
||||||
self.call(building.CmdFind(), "Char2", expect, cmdstring="locate")
|
self.call(building.CmdFind(), "Char2", expect, cmdstring="locate")
|
||||||
|
|
@ -420,11 +558,43 @@ class TestBuilding(CommandTest):
|
||||||
self.call(building.CmdFind(), "Char2", "One Match", cmdstring="@find")
|
self.call(building.CmdFind(), "Char2", "One Match", cmdstring="@find")
|
||||||
self.call(building.CmdFind(), "/startswith Room2", "One Match")
|
self.call(building.CmdFind(), "/startswith Room2", "One Match")
|
||||||
|
|
||||||
|
self.call(building.CmdFind(), self.char1.dbref,
|
||||||
|
"Exact dbref match(#1-#7):\n "
|
||||||
|
" Char(#6) - evennia.objects.objects.DefaultCharacter")
|
||||||
|
self.call(building.CmdFind(), "*TestAccount",
|
||||||
|
"Match(#1-#7):\n"
|
||||||
|
" TestAccount - evennia.accounts.accounts.DefaultAccount")
|
||||||
|
|
||||||
|
self.call(building.CmdFind(), "/char Obj")
|
||||||
|
self.call(building.CmdFind(), "/room Obj")
|
||||||
|
self.call(building.CmdFind(), "/exit Obj")
|
||||||
|
self.call(building.CmdFind(), "/exact Obj", "One Match")
|
||||||
|
|
||||||
def test_script(self):
|
def test_script(self):
|
||||||
|
self.call(building.CmdScript(), "Obj = ", "No scripts defined on Obj")
|
||||||
self.call(building.CmdScript(), "Obj = scripts.Script", "Script scripts.Script successfully added")
|
self.call(building.CmdScript(), "Obj = scripts.Script", "Script scripts.Script successfully added")
|
||||||
|
self.call(building.CmdScript(), "", "Usage: ")
|
||||||
|
self.call(building.CmdScript(), "= Obj", "To create a global script you need @scripts/add <typeclass>.")
|
||||||
|
self.call(building.CmdScript(), "Obj = ", "dbref obj")
|
||||||
|
|
||||||
|
self.call(building.CmdScript(), "/start Obj", "0 scripts started on Obj") # because it's already started
|
||||||
|
self.call(building.CmdScript(), "/stop Obj", "Stopping script")
|
||||||
|
|
||||||
|
self.call(building.CmdScript(), "Obj = scripts.Script", "Script scripts.Script successfully added")
|
||||||
|
self.call(building.CmdScript(), "/start Obj = scripts.Script", "Script scripts.Script could not be (re)started.")
|
||||||
|
self.call(building.CmdScript(), "/stop Obj = scripts.Script", "Script stopped and removed from object.")
|
||||||
|
|
||||||
def test_teleport(self):
|
def test_teleport(self):
|
||||||
self.call(building.CmdTeleport(), "/quiet Room2", "Room2(#2)\n|Teleported to Room2.")
|
self.call(building.CmdTeleport(), "", "Usage: ")
|
||||||
|
self.call(building.CmdTeleport(), "Obj = Room", "Obj is already at Room.")
|
||||||
|
self.call(building.CmdTeleport(), "Obj = NotFound", "Could not find 'NotFound'.|Destination not found.")
|
||||||
|
self.call(building.CmdTeleport(),
|
||||||
|
"Obj = Room2", "Obj(#4) is leaving Room(#1), heading for Room2(#2).|Teleported Obj -> Room2.")
|
||||||
|
self.call(building.CmdTeleport(), "NotFound = Room", "Could not find 'NotFound'.")
|
||||||
|
self.call(building.CmdTeleport(), "Obj = Obj", "You can't teleport an object inside of itself!")
|
||||||
|
|
||||||
|
self.call(building.CmdTeleport(), "/tonone Obj2", "Teleported Obj2 -> None-location.")
|
||||||
|
self.call(building.CmdTeleport(), "/quiet Room2", "Room2(#2)")
|
||||||
self.call(building.CmdTeleport(), "/t", # /t switch is abbreviated form of /tonone
|
self.call(building.CmdTeleport(), "/t", # /t switch is abbreviated form of /tonone
|
||||||
"Cannot teleport a puppeted object (Char, puppeted by TestAccount(account 1)) to a None-location.")
|
"Cannot teleport a puppeted object (Char, puppeted by TestAccount(account 1)) to a None-location.")
|
||||||
self.call(building.CmdTeleport(), "/l Room2", # /l switch is abbreviated form of /loc
|
self.call(building.CmdTeleport(), "/l Room2", # /l switch is abbreviated form of /loc
|
||||||
|
|
@ -432,6 +602,28 @@ class TestBuilding(CommandTest):
|
||||||
self.call(building.CmdTeleport(), "/q me to Room2", # /q switch is abbreviated form of /quiet
|
self.call(building.CmdTeleport(), "/q me to Room2", # /q switch is abbreviated form of /quiet
|
||||||
"Char is already at Room2.")
|
"Char is already at Room2.")
|
||||||
|
|
||||||
|
def test_tag(self):
|
||||||
|
self.call(building.CmdTag(), "", "Usage: ")
|
||||||
|
|
||||||
|
self.call(building.CmdTag(), "Obj = testtag")
|
||||||
|
self.call(building.CmdTag(), "Obj = testtag2")
|
||||||
|
self.call(building.CmdTag(), "Obj = testtag2:category1")
|
||||||
|
self.call(building.CmdTag(), "Obj = testtag3")
|
||||||
|
|
||||||
|
self.call(building.CmdTag(), "Obj", "Tags on Obj: 'testtag', 'testtag2', "
|
||||||
|
"'testtag2' (category: category1), 'testtag3'")
|
||||||
|
|
||||||
|
self.call(building.CmdTag(), "/search NotFound", "No objects found with tag 'NotFound'.")
|
||||||
|
self.call(building.CmdTag(), "/search testtag", "Found 1 object with tag 'testtag':")
|
||||||
|
self.call(building.CmdTag(), "/search testtag2", "Found 1 object with tag 'testtag2':")
|
||||||
|
self.call(building.CmdTag(), "/search testtag2:category1",
|
||||||
|
"Found 1 object with tag 'testtag2' (category: 'category1'):")
|
||||||
|
|
||||||
|
|
||||||
|
self.call(building.CmdTag(), "/del Obj = testtag3", "Removed tag 'testtag3' from Obj.")
|
||||||
|
self.call(building.CmdTag(), "/del Obj",
|
||||||
|
"Cleared all tags from Obj: testtag, testtag2, testtag2 (category: category1)")
|
||||||
|
|
||||||
def test_spawn(self):
|
def test_spawn(self):
|
||||||
def getObject(commandTest, objKeyStr):
|
def getObject(commandTest, objKeyStr):
|
||||||
# A helper function to get a spawned object and
|
# A helper function to get a spawned object and
|
||||||
|
|
@ -453,6 +645,14 @@ class TestBuilding(CommandTest):
|
||||||
"'typeclass':'evennia.objects.objects.DefaultCharacter'}",
|
"'typeclass':'evennia.objects.objects.DefaultCharacter'}",
|
||||||
"Saved prototype: testprot", inputs=['y'])
|
"Saved prototype: testprot", inputs=['y'])
|
||||||
|
|
||||||
|
self.call(building.CmdSpawn(), "/search ", "Key ")
|
||||||
|
self.call(building.CmdSpawn(), "/search test;test2", "")
|
||||||
|
|
||||||
|
self.call(building.CmdSpawn(),
|
||||||
|
"/save {'key':'Test Char', "
|
||||||
|
"'typeclass':'evennia.objects.objects.DefaultCharacter'}",
|
||||||
|
"To save a prototype it must have the 'prototype_key' set.")
|
||||||
|
|
||||||
self.call(building.CmdSpawn(), "/list", "Key ")
|
self.call(building.CmdSpawn(), "/list", "Key ")
|
||||||
|
|
||||||
self.call(building.CmdSpawn(), 'testprot', "Spawned Test Char")
|
self.call(building.CmdSpawn(), 'testprot', "Spawned Test Char")
|
||||||
|
|
@ -480,9 +680,9 @@ class TestBuilding(CommandTest):
|
||||||
goblin.delete()
|
goblin.delete()
|
||||||
|
|
||||||
# create prototype
|
# create prototype
|
||||||
protlib.create_prototype(**{'key': 'Ball',
|
protlib.create_prototype({'key': 'Ball',
|
||||||
'typeclass': 'evennia.objects.objects.DefaultCharacter',
|
'typeclass': 'evennia.objects.objects.DefaultCharacter',
|
||||||
'prototype_key': 'testball'})
|
'prototype_key': 'testball'})
|
||||||
|
|
||||||
# Tests "@spawn <prototype_name>"
|
# Tests "@spawn <prototype_name>"
|
||||||
self.call(building.CmdSpawn(), "testball", "Spawned Ball")
|
self.call(building.CmdSpawn(), "testball", "Spawned Ball")
|
||||||
|
|
|
||||||
|
|
@ -90,7 +90,7 @@ def display_meter(cur_value, max_value,
|
||||||
|
|
||||||
# Pick which fill color to use based on how full the bar is
|
# Pick which fill color to use based on how full the bar is
|
||||||
fillcolor_index = (float(len(fill_color)) * percent_full)
|
fillcolor_index = (float(len(fill_color)) * percent_full)
|
||||||
fillcolor_index = int(round(fillcolor_index)) - 1
|
fillcolor_index = max(0, int(round(fillcolor_index)) - 1)
|
||||||
fillcolor_code = "|[" + fill_color[fillcolor_index]
|
fillcolor_code = "|[" + fill_color[fillcolor_index]
|
||||||
|
|
||||||
# Make color codes for empty bar portion and text_color
|
# Make color codes for empty bar portion and text_color
|
||||||
|
|
|
||||||
|
|
@ -2,17 +2,18 @@
|
||||||
Module containing the test cases for the Audit system.
|
Module containing the test cases for the Audit system.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
from django.test import override_settings
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from evennia.utils.test_resources import EvenniaTest
|
from evennia.utils.test_resources import EvenniaTest
|
||||||
import re
|
import re
|
||||||
|
|
||||||
# Configure session auditing settings
|
# Configure session auditing settings - TODO: This is bad practice that leaks over to other tests
|
||||||
settings.AUDIT_CALLBACK = "evennia.security.contrib.auditing.outputs.to_syslog"
|
settings.AUDIT_CALLBACK = "evennia.security.contrib.auditing.outputs.to_syslog"
|
||||||
settings.AUDIT_IN = True
|
settings.AUDIT_IN = True
|
||||||
settings.AUDIT_OUT = True
|
settings.AUDIT_OUT = True
|
||||||
settings.AUDIT_ALLOW_SPARSE = True
|
settings.AUDIT_ALLOW_SPARSE = True
|
||||||
|
|
||||||
# Configure settings to use custom session
|
# Configure settings to use custom session - TODO: This is bad practice, changing global settings
|
||||||
settings.SERVER_SESSION_CLASS = "evennia.contrib.security.auditing.server.AuditedServerSession"
|
settings.SERVER_SESSION_CLASS = "evennia.contrib.security.auditing.server.AuditedServerSession"
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -691,8 +691,21 @@ from evennia.contrib import health_bar
|
||||||
|
|
||||||
class TestHealthBar(EvenniaTest):
|
class TestHealthBar(EvenniaTest):
|
||||||
def test_healthbar(self):
|
def test_healthbar(self):
|
||||||
expected_bar_str = "|[G|w |n|[B|w test24 / 200test |n"
|
expected_bar_str = '|[R|w|n|[B|w test0 / 200test |n'
|
||||||
self.assertTrue(health_bar.display_meter(24, 200, length=40, pre_text="test", post_text="test", align="center") == expected_bar_str)
|
self.assertEqual(health_bar.display_meter(
|
||||||
|
0, 200, length=40, pre_text="test", post_text="test", align="center"), expected_bar_str)
|
||||||
|
expected_bar_str = "|[R|w |n|[B|w test24 / 200test |n"
|
||||||
|
self.assertEqual(health_bar.display_meter(
|
||||||
|
24, 200, length=40, pre_text="test", post_text="test", align="center"), expected_bar_str)
|
||||||
|
expected_bar_str = '|[Y|w test100 /|n|[B|w 200test |n'
|
||||||
|
self.assertEqual(health_bar.display_meter(
|
||||||
|
100, 200, length=40, pre_text="test", post_text="test", align="center"), expected_bar_str)
|
||||||
|
expected_bar_str = '|[G|w test180 / 200test |n|[B|w |n'
|
||||||
|
self.assertEqual(health_bar.display_meter(
|
||||||
|
180, 200, length=40, pre_text="test", post_text="test", align="center"), expected_bar_str)
|
||||||
|
expected_bar_str = '|[G|w test200 / 200test |n|[B|w|n'
|
||||||
|
self.assertEqual(health_bar.display_meter(
|
||||||
|
200, 200, length=40, pre_text="test", post_text="test", align="center"), expected_bar_str)
|
||||||
|
|
||||||
# test mail contrib
|
# test mail contrib
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -177,7 +177,7 @@ start
|
||||||
#
|
#
|
||||||
# This room inherits from a Typeclass called WeatherRoom. It regularly
|
# This room inherits from a Typeclass called WeatherRoom. It regularly
|
||||||
# and randomly shows some weather effects. Note how we can spread the
|
# and randomly shows some weather effects. Note how we can spread the
|
||||||
# command's arguments over more than one line for easy reading. we
|
# command's arguments over more than one line for easy reading. We
|
||||||
# also make sure to create plenty of aliases for the room and
|
# also make sure to create plenty of aliases for the room and
|
||||||
# exits. Note the alias tut#02: this unique identifier can be used
|
# exits. Note the alias tut#02: this unique identifier can be used
|
||||||
# later in the script to always find the way back to this room (for
|
# later in the script to always find the way back to this room (for
|
||||||
|
|
@ -420,10 +420,6 @@ north
|
||||||
|
|
||||||
(to get a weapon from the barrel, use |wget weapon|n)
|
(to get a weapon from the barrel, use |wget weapon|n)
|
||||||
#
|
#
|
||||||
@desc barrel =
|
|
||||||
This barrel has the air of leftovers - it contains an assorted
|
|
||||||
mess of random weaponry in various states and qualities.
|
|
||||||
#
|
|
||||||
@detail barkeep;man;landlord =
|
@detail barkeep;man;landlord =
|
||||||
The landlord is a cheerful fellow, always ready to supply you with
|
The landlord is a cheerful fellow, always ready to supply you with
|
||||||
more beer. He mentions doing some sort of arcane magic known as
|
more beer. He mentions doing some sort of arcane magic known as
|
||||||
|
|
@ -439,8 +435,17 @@ north
|
||||||
#
|
#
|
||||||
@create/drop barrel: tutorial_world.objects.WeaponRack
|
@create/drop barrel: tutorial_world.objects.WeaponRack
|
||||||
#
|
#
|
||||||
|
@desc barrel =
|
||||||
|
This barrel has the air of leftovers - it contains an assorted
|
||||||
|
mess of random weaponry in various states and qualities.
|
||||||
|
#
|
||||||
@lock barrel = get:false()
|
@lock barrel = get:false()
|
||||||
#
|
#
|
||||||
|
# Players trying to pickup barrel will receive hint to 'get weapon' instead
|
||||||
|
#
|
||||||
|
@set barrel/get_err_msg =
|
||||||
|
The barkeep shakes his head. He says: 'Get weapon, not the barrel.'
|
||||||
|
#
|
||||||
# This id makes sure that we cannot pick more than one weapon from this rack
|
# This id makes sure that we cannot pick more than one weapon from this rack
|
||||||
#
|
#
|
||||||
@set barrel/rack_id = "rack_barrel"
|
@set barrel/rack_id = "rack_barrel"
|
||||||
|
|
@ -836,7 +841,7 @@ archway
|
||||||
#
|
#
|
||||||
# Set its home to this location
|
# Set its home to this location
|
||||||
#
|
#
|
||||||
@home ghost = tut#11
|
@sethome ghost = tut#11
|
||||||
#
|
#
|
||||||
@lock ghost = get:false()
|
@lock ghost = get:false()
|
||||||
#
|
#
|
||||||
|
|
|
||||||
|
|
@ -37,7 +37,7 @@ class CmdMobOnOff(Command):
|
||||||
to turn on/off the mob."
|
to turn on/off the mob."
|
||||||
"""
|
"""
|
||||||
if not self.args:
|
if not self.args:
|
||||||
self.caller.msg("Usage: mobon|moboff <mob>")
|
self.caller.msg("Usage: mobon||moboff <mob>")
|
||||||
return
|
return
|
||||||
mob = self.caller.search(self.args)
|
mob = self.caller.search(self.args)
|
||||||
if not mob:
|
if not mob:
|
||||||
|
|
|
||||||
BIN
evennia/locale/pl/LC_MESSAGES/django.mo
Normal file
BIN
evennia/locale/pl/LC_MESSAGES/django.mo
Normal file
Binary file not shown.
315
evennia/locale/pl/LC_MESSAGES/django.po
Normal file
315
evennia/locale/pl/LC_MESSAGES/django.po
Normal file
|
|
@ -0,0 +1,315 @@
|
||||||
|
# Polish localization for the EVENNIA.
|
||||||
|
# Copyright (C) 2019
|
||||||
|
# This file is distributed under the same license as the PACKAGE package.
|
||||||
|
# FIRST AUTHOR <akroczyk@gmail.com>, 2019.
|
||||||
|
#
|
||||||
|
msgid ""
|
||||||
|
msgstr ""
|
||||||
|
"Project-Id-Version: ArkMUD Polish translation v0.1\n"
|
||||||
|
"Report-Msgid-Bugs-To: \n"
|
||||||
|
"POT-Creation-Date: 2019-02-20 12:13+0000\n"
|
||||||
|
"PO-Revision-Date: 2019-02-20 14:18+0100\n"
|
||||||
|
"Language: pl_PL\n"
|
||||||
|
"MIME-Version: 1.0\n"
|
||||||
|
"Content-Type: text/plain; charset=UTF-8\n"
|
||||||
|
"Content-Transfer-Encoding: 8bit\n"
|
||||||
|
"Plural-Forms: nplurals=4; plural=(n==1 ? 0 : (n%10>=2 && n%10<=4) && (n"
|
||||||
|
"%100<12 || n%100>=14) ? 1 : n!=1 && (n%10>=0 && n%10<=1) || (n%10>=5 && n"
|
||||||
|
"%10<=9) || (n%100>=12 && n%100<=14) ? 2 : 3);\n"
|
||||||
|
"Last-Translator: \n"
|
||||||
|
"Language-Team: ArkMUD team\n"
|
||||||
|
"X-Generator: Poedit 2.2.1\n"
|
||||||
|
|
||||||
|
#: accounts/accounts.py:440
|
||||||
|
msgid "Account being deleted."
|
||||||
|
msgstr "Konto zostalo usuniete."
|
||||||
|
|
||||||
|
#: commands/cmdhandler.py:681
|
||||||
|
msgid "There were multiple matches."
|
||||||
|
msgstr "Znaleziono wiele dopasowan."
|
||||||
|
|
||||||
|
#: commands/cmdhandler.py:704
|
||||||
|
#, python-format
|
||||||
|
msgid "Command '%s' is not available."
|
||||||
|
msgstr "Komenda '%s' jests niedostepna."
|
||||||
|
|
||||||
|
#: commands/cmdhandler.py:709
|
||||||
|
#, python-format
|
||||||
|
msgid " Maybe you meant %s?"
|
||||||
|
msgstr " Czy miales na mysli %s?"
|
||||||
|
|
||||||
|
#: commands/cmdhandler.py:709
|
||||||
|
msgid "or"
|
||||||
|
msgstr "lub"
|
||||||
|
|
||||||
|
#: commands/cmdhandler.py:711
|
||||||
|
msgid " Type \"help\" for help."
|
||||||
|
msgstr " Wpisz \"help\" aby otworzyc pomoc."
|
||||||
|
|
||||||
|
#: commands/cmdsethandler.py:89
|
||||||
|
#, python-brace-format
|
||||||
|
msgid ""
|
||||||
|
"{traceback}\n"
|
||||||
|
"Error loading cmdset '{path}'\n"
|
||||||
|
"(Traceback was logged {timestamp})"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: commands/cmdsethandler.py:94
|
||||||
|
#, python-brace-format
|
||||||
|
msgid ""
|
||||||
|
"Error loading cmdset: No cmdset class '{classname}' in '{path}'.\n"
|
||||||
|
"(Traceback was logged {timestamp})"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: commands/cmdsethandler.py:98
|
||||||
|
#, python-brace-format
|
||||||
|
msgid ""
|
||||||
|
"{traceback}\n"
|
||||||
|
"SyntaxError encountered when loading cmdset '{path}'.\n"
|
||||||
|
"(Traceback was logged {timestamp})"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: commands/cmdsethandler.py:103
|
||||||
|
#, python-brace-format
|
||||||
|
msgid ""
|
||||||
|
"{traceback}\n"
|
||||||
|
"Compile/Run error when loading cmdset '{path}'.\",\n"
|
||||||
|
"(Traceback was logged {timestamp})"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: commands/cmdsethandler.py:108
|
||||||
|
#, python-brace-format
|
||||||
|
msgid ""
|
||||||
|
"\n"
|
||||||
|
"Error encountered for cmdset at path '{path}'.\n"
|
||||||
|
"Replacing with fallback '{fallback_path}'.\n"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: commands/cmdsethandler.py:114
|
||||||
|
#, python-brace-format
|
||||||
|
msgid "Fallback path '{fallback_path}' failed to generate a cmdset."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: commands/cmdsethandler.py:182 commands/cmdsethandler.py:192
|
||||||
|
#, python-format
|
||||||
|
msgid ""
|
||||||
|
"\n"
|
||||||
|
"(Unsuccessfully tried '%s')."
|
||||||
|
msgstr ""
|
||||||
|
"\n"
|
||||||
|
"(Bezskuteczna proba '%s')."
|
||||||
|
|
||||||
|
#: commands/cmdsethandler.py:311
|
||||||
|
#, python-brace-format
|
||||||
|
msgid "custom {mergetype} on cmdset '{cmdset}'"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: commands/cmdsethandler.py:314
|
||||||
|
#, python-brace-format
|
||||||
|
msgid " <Merged {mergelist} {mergetype}, prio {prio}>: {current}"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: commands/cmdsethandler.py:322
|
||||||
|
#, python-brace-format
|
||||||
|
msgid ""
|
||||||
|
" <{key} ({mergetype}, prio {prio}, {permstring})>:\n"
|
||||||
|
" {keylist}"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: commands/cmdsethandler.py:426
|
||||||
|
msgid "Only CmdSets can be added to the cmdsethandler!"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: comms/channelhandler.py:100
|
||||||
|
msgid "Say what?"
|
||||||
|
msgstr "Ze co?"
|
||||||
|
|
||||||
|
#: comms/channelhandler.py:105
|
||||||
|
#, python-format
|
||||||
|
msgid "Channel '%s' not found."
|
||||||
|
msgstr "Kanal '%s' nie odnaleziony."
|
||||||
|
|
||||||
|
#: comms/channelhandler.py:108
|
||||||
|
#, python-format
|
||||||
|
msgid "You are not connected to channel '%s'."
|
||||||
|
msgstr "Nie jestes polaczony z kanalem '%s'."
|
||||||
|
|
||||||
|
#: comms/channelhandler.py:112
|
||||||
|
#, python-format
|
||||||
|
msgid "You are not permitted to send to channel '%s'."
|
||||||
|
msgstr "Nie masz uprawnien do wysylania wiadomosci na kanal '%s'."
|
||||||
|
|
||||||
|
#: comms/channelhandler.py:155
|
||||||
|
msgid " (channel)"
|
||||||
|
msgstr " (kanal)"
|
||||||
|
|
||||||
|
#: locks/lockhandler.py:236
|
||||||
|
#, python-format
|
||||||
|
msgid "Lock: lock-function '%s' is not available."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: locks/lockhandler.py:249
|
||||||
|
#, python-format
|
||||||
|
msgid "Lock: definition '%s' has syntax errors."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: locks/lockhandler.py:253
|
||||||
|
#, python-format
|
||||||
|
msgid ""
|
||||||
|
"LockHandler on %(obj)s: access type '%(access_type)s' changed from "
|
||||||
|
"'%(source)s' to '%(goal)s' "
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: locks/lockhandler.py:320
|
||||||
|
#, python-brace-format
|
||||||
|
msgid "Lock: '{lockdef}' contains no colon (:)."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: locks/lockhandler.py:328
|
||||||
|
#, python-brace-format
|
||||||
|
msgid "Lock: '{lockdef}' has no access_type (left-side of colon is empty)."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: locks/lockhandler.py:336
|
||||||
|
#, python-brace-format
|
||||||
|
msgid "Lock: '{lockdef}' has mismatched parentheses."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: locks/lockhandler.py:343
|
||||||
|
#, python-brace-format
|
||||||
|
msgid "Lock: '{lockdef}' has no valid lock functions."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: objects/objects.py:732
|
||||||
|
#, python-format
|
||||||
|
msgid "Couldn't perform move ('%s'). Contact an admin."
|
||||||
|
msgstr "Nie udalo sie wykonac ruchu ('%s'). Skontaktuj sie z adminem."
|
||||||
|
|
||||||
|
#: objects/objects.py:742
|
||||||
|
msgid "The destination doesn't exist."
|
||||||
|
msgstr "Punkt przeznaczenia nie istnieje."
|
||||||
|
|
||||||
|
#: objects/objects.py:833
|
||||||
|
#, python-format
|
||||||
|
msgid "Could not find default home '(#%d)'."
|
||||||
|
msgstr "Nie znaleziono domyslnego domu '(#%d)'."
|
||||||
|
|
||||||
|
#: objects/objects.py:849
|
||||||
|
msgid "Something went wrong! You are dumped into nowhere. Contact an admin."
|
||||||
|
msgstr "Cos poszlo zle! Zostales wrzucony w nicosc. Skontaktuj sie z adminem."
|
||||||
|
|
||||||
|
#: objects/objects.py:915
|
||||||
|
#, python-format
|
||||||
|
msgid "Your character %s has been destroyed."
|
||||||
|
msgstr "Twoja postac %s zostala zniszczona."
|
||||||
|
|
||||||
|
#: scripts/scripthandler.py:53
|
||||||
|
#, python-format
|
||||||
|
msgid ""
|
||||||
|
"\n"
|
||||||
|
" '%(key)s' (%(next_repeat)s/%(interval)s, %(repeats)s repeats): %(desc)s"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: scripts/scripts.py:205
|
||||||
|
#, python-format
|
||||||
|
msgid ""
|
||||||
|
"Script %(key)s(#%(dbid)s) of type '%(cname)s': at_repeat() error '%(err)s'."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: server/initial_setup.py:28
|
||||||
|
msgid ""
|
||||||
|
"\n"
|
||||||
|
"Welcome to your new |wEvennia|n-based game! Visit http://www.evennia.com if "
|
||||||
|
"you need\n"
|
||||||
|
"help, want to contribute, report issues or just join the community.\n"
|
||||||
|
"As Account #1 you can create a demo/tutorial area with |w@batchcommand "
|
||||||
|
"tutorial_world.build|n.\n"
|
||||||
|
" "
|
||||||
|
msgstr ""
|
||||||
|
"\n"
|
||||||
|
"Witaj w swojej nowej grze, bazujacej na |wEvennia|n! Odwiedz http://www."
|
||||||
|
"evennia.com\n"
|
||||||
|
"jesli potrzebujesz pomocy, chcesz pomoc badz zglosic blad, lub po prostu "
|
||||||
|
"chcesz dolaczyc do spolecznosci.\n"
|
||||||
|
"Jako Konto #1 mozesz otworzyc demo/samouczek wpisujac |w@batchcommand "
|
||||||
|
"tutorial_world.build|n.\n"
|
||||||
|
" "
|
||||||
|
|
||||||
|
#: server/initial_setup.py:92
|
||||||
|
msgid "This is User #1."
|
||||||
|
msgstr "To jest User #1."
|
||||||
|
|
||||||
|
#: server/initial_setup.py:105
|
||||||
|
msgid "Limbo"
|
||||||
|
msgstr "Otchlan"
|
||||||
|
|
||||||
|
#: server/server.py:139
|
||||||
|
msgid "idle timeout exceeded"
|
||||||
|
msgstr "czas bezczynnosci przekroczony"
|
||||||
|
|
||||||
|
#: server/sessionhandler.py:386
|
||||||
|
msgid " ... Server restarted."
|
||||||
|
msgstr " ... Serwer zrestartowany."
|
||||||
|
|
||||||
|
#: server/sessionhandler.py:606
|
||||||
|
msgid "Logged in from elsewhere. Disconnecting."
|
||||||
|
msgstr "Zalogowano z innego miejsca. Rozlaczanie."
|
||||||
|
|
||||||
|
#: server/sessionhandler.py:634
|
||||||
|
msgid "Idle timeout exceeded, disconnecting."
|
||||||
|
msgstr "Czas bezczynnosci przekroczony, rozlaczanie."
|
||||||
|
|
||||||
|
#: server/validators.py:50
|
||||||
|
#, python-format
|
||||||
|
msgid ""
|
||||||
|
"%s From a terminal client, you can also use a phrase of multiple words if "
|
||||||
|
"you enclose the password in double quotes."
|
||||||
|
msgstr ""
|
||||||
|
"%s Z poziomu terminala, mozesz rowniez uzyc frazy z wieloma slowami jesli "
|
||||||
|
"ujmiesz haslo w cudzyslowie."
|
||||||
|
|
||||||
|
#: utils/evmenu.py:192
|
||||||
|
#, python-brace-format
|
||||||
|
msgid ""
|
||||||
|
"Menu node '{nodename}' is either not implemented or caused an error. Make "
|
||||||
|
"another choice."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: utils/evmenu.py:194
|
||||||
|
#, python-brace-format
|
||||||
|
msgid "Error in menu node '{nodename}'."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: utils/evmenu.py:195
|
||||||
|
msgid "No description."
|
||||||
|
msgstr "Brak opisu."
|
||||||
|
|
||||||
|
#: utils/evmenu.py:196
|
||||||
|
msgid "Commands: <menu option>, help, quit"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: utils/evmenu.py:197
|
||||||
|
msgid "Commands: <menu option>, help"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: utils/evmenu.py:198
|
||||||
|
msgid "Commands: help, quit"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: utils/evmenu.py:199
|
||||||
|
msgid "Commands: help"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: utils/evmenu.py:200
|
||||||
|
msgid "Choose an option or try 'help'."
|
||||||
|
msgstr "Wybierz opcje lub uzyj komendy 'help'."
|
||||||
|
|
||||||
|
#: utils/utils.py:1866
|
||||||
|
#, python-format
|
||||||
|
msgid "Could not find '%s'."
|
||||||
|
msgstr "Nie odnaleziono '%s'."
|
||||||
|
|
||||||
|
#: utils/utils.py:1873
|
||||||
|
#, python-format
|
||||||
|
msgid "More than one match for '%s' (please narrow target):\n"
|
||||||
|
msgstr "Wiecej niz jedno dopasowanie dla '%s' (prosze zawezyc cel):\n"
|
||||||
|
|
@ -69,7 +69,7 @@ class ObjectDBManager(TypedObjectManager):
|
||||||
object candidates.
|
object candidates.
|
||||||
|
|
||||||
Return:
|
Return:
|
||||||
match (Object or list): One or more matching results.
|
match (query): Matching query.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
ostring = str(ostring).lstrip('*')
|
ostring = str(ostring).lstrip('*')
|
||||||
|
|
@ -77,7 +77,7 @@ class ObjectDBManager(TypedObjectManager):
|
||||||
dbref = self.dbref(ostring)
|
dbref = self.dbref(ostring)
|
||||||
if dbref:
|
if dbref:
|
||||||
try:
|
try:
|
||||||
return self.get(id=dbref)
|
return self.get(db_account__id=dbref)
|
||||||
except self.model.DoesNotExist:
|
except self.model.DoesNotExist:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
@ -87,13 +87,13 @@ class ObjectDBManager(TypedObjectManager):
|
||||||
if exact:
|
if exact:
|
||||||
return self.filter(cand_restriction & Q(db_account__username__iexact=ostring))
|
return self.filter(cand_restriction & Q(db_account__username__iexact=ostring))
|
||||||
else: # fuzzy matching
|
else: # fuzzy matching
|
||||||
ply_cands = self.filter(cand_restriction & Q(accountdb__username__istartswith=ostring)
|
obj_cands = self.select_related().filter(cand_restriction & Q(db_account__username__istartswith=ostring))
|
||||||
).values_list("db_key", flat=True)
|
acct_cands = [obj.account for obj in obj_cands]
|
||||||
if candidates:
|
|
||||||
index_matches = string_partial_matching(ply_cands, ostring, ret_index=True)
|
if obj_cands:
|
||||||
return [obj for ind, obj in enumerate(make_iter(candidates)) if ind in index_matches]
|
index_matches = string_partial_matching([acct.key for acct in acct_cands], ostring, ret_index=True)
|
||||||
else:
|
acct_cands = [acct_cands[i].id for i in index_matches]
|
||||||
return string_partial_matching(ply_cands, ostring, ret_index=False)
|
return obj_cands.filter(db_account__id__in=acct_cands)
|
||||||
|
|
||||||
def get_objs_with_key_and_typeclass(self, oname, otypeclass_path, candidates=None):
|
def get_objs_with_key_and_typeclass(self, oname, otypeclass_path, candidates=None):
|
||||||
"""
|
"""
|
||||||
|
|
@ -105,7 +105,7 @@ class ObjectDBManager(TypedObjectManager):
|
||||||
candidates (list, optional): Only match among the given list of candidates.
|
candidates (list, optional): Only match among the given list of candidates.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
matches (list): The matching objects.
|
matches (query): The matching objects.
|
||||||
"""
|
"""
|
||||||
cand_restriction = candidates is not None and Q(pk__in=[_GA(obj, "id") for obj in make_iter(candidates)
|
cand_restriction = candidates is not None and Q(pk__in=[_GA(obj, "id") for obj in make_iter(candidates)
|
||||||
if obj]) or Q()
|
if obj]) or Q()
|
||||||
|
|
@ -119,18 +119,19 @@ class ObjectDBManager(TypedObjectManager):
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
attribute_name (str): Attribute name to search for.
|
attribute_name (str): Attribute name to search for.
|
||||||
candidates (list, optional): Only match among the given list of candidates.
|
candidates (list, optional): Only match among the given list of object
|
||||||
|
candidates.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
matches (list): All objects having the given attribute_name defined at all.
|
matches (query): All objects having the given attribute_name defined at all.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
cand_restriction = candidates is not None and Q(db_attributes__db_obj__pk__in=[_GA(obj, "id") for obj
|
cand_restriction = \
|
||||||
in make_iter(candidates)
|
candidates is not None and Q(id__in=[obj.id for obj in candidates]) or Q()
|
||||||
if obj]) or Q()
|
return self.filter(cand_restriction & Q(db_attributes__db_key=attribute_name))
|
||||||
return list(self.filter(cand_restriction & Q(db_attributes__db_key=attribute_name)))
|
|
||||||
|
|
||||||
def get_objs_with_attr_value(self, attribute_name, attribute_value, candidates=None, typeclasses=None):
|
def get_objs_with_attr_value(self, attribute_name, attribute_value,
|
||||||
|
candidates=None, typeclasses=None):
|
||||||
"""
|
"""
|
||||||
Get all objects having the given attrname set to the given value.
|
Get all objects having the given attrname set to the given value.
|
||||||
|
|
||||||
|
|
@ -141,7 +142,8 @@ class ObjectDBManager(TypedObjectManager):
|
||||||
typeclasses (list, optional): Python pats to restrict matches with.
|
typeclasses (list, optional): Python pats to restrict matches with.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
matches (list): Objects fullfilling both the `attribute_name` and `attribute_value` criterions.
|
matches (list): Objects fullfilling both the `attribute_name` and
|
||||||
|
`attribute_value` criterions.
|
||||||
|
|
||||||
Notes:
|
Notes:
|
||||||
This uses the Attribute's PickledField to transparently search the database by matching
|
This uses the Attribute's PickledField to transparently search the database by matching
|
||||||
|
|
|
||||||
|
|
@ -262,7 +262,13 @@ class DefaultObject(with_metaclass(TypeclassBase, ObjectDB)):
|
||||||
con = self.contents_cache.get(exclude=exclude)
|
con = self.contents_cache.get(exclude=exclude)
|
||||||
# print "contents_get:", self, con, id(self), calledby() # DEBUG
|
# print "contents_get:", self, con, id(self), calledby() # DEBUG
|
||||||
return con
|
return con
|
||||||
contents = property(contents_get)
|
|
||||||
|
def contents_set(self, *args):
|
||||||
|
"You cannot replace this property"
|
||||||
|
raise AttributeError("{}.contents is read-only. Use obj.move_to or "
|
||||||
|
"obj.location to move an object here.".format(self.__class__))
|
||||||
|
|
||||||
|
contents = property(contents_get, contents_set, contents_set)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def exits(self):
|
def exits(self):
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
from evennia.utils.test_resources import EvenniaTest
|
from evennia.utils.test_resources import EvenniaTest
|
||||||
from evennia import DefaultObject, DefaultCharacter, DefaultRoom, DefaultExit
|
from evennia import DefaultObject, DefaultCharacter, DefaultRoom, DefaultExit
|
||||||
|
from evennia.objects.models import ObjectDB
|
||||||
|
|
||||||
|
|
||||||
class DefaultObjectTest(EvenniaTest):
|
class DefaultObjectTest(EvenniaTest):
|
||||||
|
|
@ -45,3 +46,48 @@ class DefaultObjectTest(EvenniaTest):
|
||||||
|
|
||||||
self.assertTrue(self.room1.get_absolute_url())
|
self.assertTrue(self.room1.get_absolute_url())
|
||||||
self.assertTrue('admin' in self.room1.web_get_admin_url())
|
self.assertTrue('admin' in self.room1.web_get_admin_url())
|
||||||
|
|
||||||
|
|
||||||
|
class TestObjectManager(EvenniaTest):
|
||||||
|
"Test object manager methods"
|
||||||
|
def test_get_object_with_account(self):
|
||||||
|
query = ObjectDB.objects.get_object_with_account("TestAccount").first()
|
||||||
|
self.assertEqual(query, self.char1)
|
||||||
|
query = ObjectDB.objects.get_object_with_account(self.account.dbref)
|
||||||
|
self.assertEqual(query, self.char1)
|
||||||
|
query = ObjectDB.objects.get_object_with_account("#123456")
|
||||||
|
self.assertFalse(query)
|
||||||
|
query = ObjectDB.objects.get_object_with_account("TestAccou").first()
|
||||||
|
self.assertFalse(query)
|
||||||
|
|
||||||
|
query = ObjectDB.objects.get_object_with_account("TestAccou", exact=False)
|
||||||
|
self.assertEqual(tuple(query), (self.char1, self.char2))
|
||||||
|
|
||||||
|
query = ObjectDB.objects.get_object_with_account(
|
||||||
|
"TestAccou", candidates=[self.char1, self.obj1], exact=False)
|
||||||
|
self.assertEqual(list(query), [self.char1])
|
||||||
|
|
||||||
|
def test_get_objs_with_key_and_typeclass(self):
|
||||||
|
query = ObjectDB.objects.get_objs_with_key_and_typeclass(
|
||||||
|
"Char", "evennia.objects.objects.DefaultCharacter")
|
||||||
|
self.assertEqual(list(query), [self.char1])
|
||||||
|
query = ObjectDB.objects.get_objs_with_key_and_typeclass(
|
||||||
|
"Char", "evennia.objects.objects.DefaultObject")
|
||||||
|
self.assertFalse(query)
|
||||||
|
query = ObjectDB.objects.get_objs_with_key_and_typeclass(
|
||||||
|
"NotFound", "evennia.objects.objects.DefaultCharacter")
|
||||||
|
self.assertFalse(query)
|
||||||
|
query = ObjectDB.objects.get_objs_with_key_and_typeclass(
|
||||||
|
"Char", "evennia.objects.objects.DefaultCharacter", candidates=[self.char1, self.char2])
|
||||||
|
self.assertEqual(list(query), [self.char1])
|
||||||
|
|
||||||
|
def test_get_objs_with_attr(self):
|
||||||
|
self.obj1.db.testattr = "testval1"
|
||||||
|
query = ObjectDB.objects.get_objs_with_attr("testattr")
|
||||||
|
self.assertEqual(list(query), [self.obj1])
|
||||||
|
query = ObjectDB.objects.get_objs_with_attr(
|
||||||
|
"testattr", candidates=[self.char1, self.obj1] )
|
||||||
|
self.assertEqual(list(query), [self.obj1])
|
||||||
|
query = ObjectDB.objects.get_objs_with_attr(
|
||||||
|
"NotFound", candidates=[self.char1, self.obj1] )
|
||||||
|
self.assertFalse(query)
|
||||||
|
|
|
||||||
|
|
@ -76,7 +76,7 @@ from evennia import prototypes
|
||||||
|
|
||||||
goblin = {"prototype_key": "goblin:, ... }
|
goblin = {"prototype_key": "goblin:, ... }
|
||||||
|
|
||||||
prototype = prototypes.save_prototype(caller, **goblin)
|
prototype = prototypes.save_prototype(goblin)
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2139,7 +2139,7 @@ def node_prototype_save(caller, **kwargs):
|
||||||
# we already validated and accepted the save, so this node acts as a goto callback and
|
# we already validated and accepted the save, so this node acts as a goto callback and
|
||||||
# should now only return the next node
|
# should now only return the next node
|
||||||
prototype_key = prototype.get("prototype_key")
|
prototype_key = prototype.get("prototype_key")
|
||||||
protlib.save_prototype(**prototype)
|
protlib.save_prototype(prototype)
|
||||||
|
|
||||||
spawned_objects = protlib.search_objects_with_prototype(prototype_key)
|
spawned_objects = protlib.search_objects_with_prototype(prototype_key)
|
||||||
nspawned = spawned_objects.count()
|
nspawned = spawned_objects.count()
|
||||||
|
|
|
||||||
|
|
@ -333,7 +333,7 @@ def objlist(*args, **kwargs):
|
||||||
def dbref(*args, **kwargs):
|
def dbref(*args, **kwargs):
|
||||||
"""
|
"""
|
||||||
Usage $dbref(<#dbref>)
|
Usage $dbref(<#dbref>)
|
||||||
Returns one Object searched globally by #dbref. Error if #dbref is invalid.
|
Validate that a #dbref input is valid.
|
||||||
"""
|
"""
|
||||||
if not args or len(args) < 1 or _RE_DBREF.match(args[0]) is None:
|
if not args or len(args) < 1 or _RE_DBREF.match(args[0]) is None:
|
||||||
raise ValueError('$dbref requires a valid #dbref argument.')
|
raise ValueError('$dbref requires a valid #dbref argument.')
|
||||||
|
|
|
||||||
|
|
@ -147,13 +147,13 @@ class DbPrototype(DefaultScript):
|
||||||
# Prototype manager functions
|
# Prototype manager functions
|
||||||
|
|
||||||
|
|
||||||
def save_prototype(**kwargs):
|
def save_prototype(prototype):
|
||||||
"""
|
"""
|
||||||
Create/Store a prototype persistently.
|
Create/Store a prototype persistently.
|
||||||
|
|
||||||
Kwargs:
|
Args:
|
||||||
prototype_key (str): This is required for any storage.
|
prototype (dict): The prototype to save. A `prototype_key` key is
|
||||||
All other kwargs are considered part of the new prototype dict.
|
required.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
prototype (dict or None): The prototype stored using the given kwargs, None if deleting.
|
prototype (dict or None): The prototype stored using the given kwargs, None if deleting.
|
||||||
|
|
@ -166,8 +166,8 @@ def save_prototype(**kwargs):
|
||||||
is expected to have valid permissions.
|
is expected to have valid permissions.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
in_prototype = prototype
|
||||||
kwargs = homogenize_prototype(kwargs)
|
in_prototype = homogenize_prototype(in_prototype)
|
||||||
|
|
||||||
def _to_batchtuple(inp, *args):
|
def _to_batchtuple(inp, *args):
|
||||||
"build tuple suitable for batch-creation"
|
"build tuple suitable for batch-creation"
|
||||||
|
|
@ -176,7 +176,7 @@ def save_prototype(**kwargs):
|
||||||
return inp
|
return inp
|
||||||
return (inp, ) + args
|
return (inp, ) + args
|
||||||
|
|
||||||
prototype_key = kwargs.get("prototype_key")
|
prototype_key = in_prototype.get("prototype_key")
|
||||||
if not prototype_key:
|
if not prototype_key:
|
||||||
raise ValidationError("Prototype requires a prototype_key")
|
raise ValidationError("Prototype requires a prototype_key")
|
||||||
|
|
||||||
|
|
@ -192,21 +192,21 @@ def save_prototype(**kwargs):
|
||||||
stored_prototype = DbPrototype.objects.filter(db_key=prototype_key)
|
stored_prototype = DbPrototype.objects.filter(db_key=prototype_key)
|
||||||
prototype = stored_prototype[0].prototype if stored_prototype else {}
|
prototype = stored_prototype[0].prototype if stored_prototype else {}
|
||||||
|
|
||||||
kwargs['prototype_desc'] = kwargs.get("prototype_desc", prototype.get("prototype_desc", ""))
|
in_prototype['prototype_desc'] = in_prototype.get("prototype_desc", prototype.get("prototype_desc", ""))
|
||||||
prototype_locks = kwargs.get(
|
prototype_locks = in_prototype.get(
|
||||||
"prototype_locks", prototype.get('prototype_locks', "spawn:all();edit:perm(Admin)"))
|
"prototype_locks", prototype.get('prototype_locks', "spawn:all();edit:perm(Admin)"))
|
||||||
is_valid, err = validate_lockstring(prototype_locks)
|
is_valid, err = validate_lockstring(prototype_locks)
|
||||||
if not is_valid:
|
if not is_valid:
|
||||||
raise ValidationError("Lock error: {}".format(err))
|
raise ValidationError("Lock error: {}".format(err))
|
||||||
kwargs['prototype_locks'] = prototype_locks
|
in_prototype['prototype_locks'] = prototype_locks
|
||||||
|
|
||||||
prototype_tags = [
|
prototype_tags = [
|
||||||
_to_batchtuple(tag, _PROTOTYPE_TAG_META_CATEGORY)
|
_to_batchtuple(tag, _PROTOTYPE_TAG_META_CATEGORY)
|
||||||
for tag in make_iter(kwargs.get("prototype_tags",
|
for tag in make_iter(in_prototype.get("prototype_tags",
|
||||||
prototype.get('prototype_tags', [])))]
|
prototype.get('prototype_tags', [])))]
|
||||||
kwargs["prototype_tags"] = prototype_tags
|
in_prototype["prototype_tags"] = prototype_tags
|
||||||
|
|
||||||
prototype.update(kwargs)
|
prototype.update(in_prototype)
|
||||||
|
|
||||||
if stored_prototype:
|
if stored_prototype:
|
||||||
# edit existing prototype
|
# edit existing prototype
|
||||||
|
|
@ -261,19 +261,25 @@ def delete_prototype(prototype_key, caller=None):
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
def search_prototype(key=None, tags=None):
|
def search_prototype(key=None, tags=None, require_single=False):
|
||||||
"""
|
"""
|
||||||
Find prototypes based on key and/or tags, or all prototypes.
|
Find prototypes based on key and/or tags, or all prototypes.
|
||||||
|
|
||||||
Kwargs:
|
Kwargs:
|
||||||
key (str): An exact or partial key to query for.
|
key (str): An exact or partial key to query for.
|
||||||
tags (str or list): Tag key or keys to query for. These
|
tags (str or list): Tag key or keys to query for. These
|
||||||
will always be applied with the 'db_protototype'
|
will always be applied with the 'db_protototype'
|
||||||
tag category.
|
tag category.
|
||||||
|
require_single (bool): If set, raise KeyError if the result
|
||||||
|
was not found or if there are multiple matches.
|
||||||
|
|
||||||
Return:
|
Return:
|
||||||
matches (list): All found prototype dicts. If no keys
|
matches (list): All found prototype dicts. Empty list if
|
||||||
or tags are given, all available prototypes will be returned.
|
no match was found. Note that if neither `key` nor `tags`
|
||||||
|
were given, *all* available prototypes will be returned.
|
||||||
|
|
||||||
|
Raises:
|
||||||
|
KeyError: If `require_single` is True and there are 0 or >1 matches.
|
||||||
|
|
||||||
Note:
|
Note:
|
||||||
The available prototypes is a combination of those supplied in
|
The available prototypes is a combination of those supplied in
|
||||||
|
|
@ -329,6 +335,10 @@ def search_prototype(key=None, tags=None):
|
||||||
if mta.get('prototype_key') and mta['prototype_key'] == key]
|
if mta.get('prototype_key') and mta['prototype_key'] == key]
|
||||||
if filter_matches and len(filter_matches) < nmatches:
|
if filter_matches and len(filter_matches) < nmatches:
|
||||||
matches = filter_matches
|
matches = filter_matches
|
||||||
|
|
||||||
|
nmatches = len(matches)
|
||||||
|
if nmatches != 1 and require_single:
|
||||||
|
raise KeyError("Found {} matching prototypes.".format(nmatches))
|
||||||
|
|
||||||
return matches
|
return matches
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -23,7 +23,7 @@ prot = {
|
||||||
"attrs": [("weapon", "sword")]
|
"attrs": [("weapon", "sword")]
|
||||||
}
|
}
|
||||||
|
|
||||||
prot = prototypes.create_prototype(**prot)
|
prot = prototypes.create_prototype(prot)
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
@ -662,8 +662,9 @@ def spawn(*prototypes, **kwargs):
|
||||||
Spawn a number of prototyped objects.
|
Spawn a number of prototyped objects.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
prototypes (dict): Each argument should be a prototype
|
prototypes (str or dict): Each argument should either be a
|
||||||
dictionary.
|
prototype_key (will be used to find the prototype) or a full prototype
|
||||||
|
dictionary. These will be batched-spawned as one object each.
|
||||||
Kwargs:
|
Kwargs:
|
||||||
prototype_modules (str or list): A python-path to a prototype
|
prototype_modules (str or list): A python-path to a prototype
|
||||||
module, or a list of such paths. These will be used to build
|
module, or a list of such paths. These will be used to build
|
||||||
|
|
@ -673,8 +674,10 @@ def spawn(*prototypes, **kwargs):
|
||||||
prototype_parents (dict): A dictionary holding a custom
|
prototype_parents (dict): A dictionary holding a custom
|
||||||
prototype-parent dictionary. Will overload same-named
|
prototype-parent dictionary. Will overload same-named
|
||||||
prototypes from prototype_modules.
|
prototypes from prototype_modules.
|
||||||
return_parents (bool): Only return a dict of the
|
return_parents (bool): Return a dict of the entire prototype-parent tree
|
||||||
prototype-parents (no object creation happens)
|
available to this prototype (no object creation happens). This is a
|
||||||
|
merged result between the globally found protparents and whatever
|
||||||
|
custom `prototype_parents` are given to this function.
|
||||||
only_validate (bool): Only run validation of prototype/parents
|
only_validate (bool): Only run validation of prototype/parents
|
||||||
(no object creation) and return the create-kwargs.
|
(no object creation) and return the create-kwargs.
|
||||||
|
|
||||||
|
|
@ -684,6 +687,11 @@ def spawn(*prototypes, **kwargs):
|
||||||
`return_parents` is set, instead return dict of prototype parents.
|
`return_parents` is set, instead return dict of prototype parents.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
# search string (=prototype_key) from input
|
||||||
|
prototypes = [protlib.search_prototype(prot, require_single=True)[0]
|
||||||
|
if isinstance(prot, basestring) else prot
|
||||||
|
for prot in prototypes]
|
||||||
|
|
||||||
# get available protparents
|
# get available protparents
|
||||||
protparents = {prot['prototype_key'].lower(): prot for prot in protlib.search_prototype()}
|
protparents = {prot['prototype_key'].lower(): prot for prot in protlib.search_prototype()}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -54,7 +54,7 @@ class TestSpawner(EvenniaTest):
|
||||||
self.prot1 = {"prototype_key": "testprototype",
|
self.prot1 = {"prototype_key": "testprototype",
|
||||||
"typeclass": "evennia.objects.objects.DefaultObject"}
|
"typeclass": "evennia.objects.objects.DefaultObject"}
|
||||||
|
|
||||||
def test_spawn(self):
|
def test_spawn_from_prot(self):
|
||||||
obj1 = spawner.spawn(self.prot1)
|
obj1 = spawner.spawn(self.prot1)
|
||||||
# check spawned objects have the right tag
|
# check spawned objects have the right tag
|
||||||
self.assertEqual(list(protlib.search_objects_with_prototype("testprototype")), obj1)
|
self.assertEqual(list(protlib.search_objects_with_prototype("testprototype")), obj1)
|
||||||
|
|
@ -62,6 +62,14 @@ class TestSpawner(EvenniaTest):
|
||||||
_PROTPARENTS["GOBLIN"], _PROTPARENTS["GOBLIN_ARCHWIZARD"],
|
_PROTPARENTS["GOBLIN"], _PROTPARENTS["GOBLIN_ARCHWIZARD"],
|
||||||
prototype_parents=_PROTPARENTS)], ['goblin grunt', 'goblin archwizard'])
|
prototype_parents=_PROTPARENTS)], ['goblin grunt', 'goblin archwizard'])
|
||||||
|
|
||||||
|
def test_spawn_from_str(self):
|
||||||
|
protlib.save_prototype(self.prot1)
|
||||||
|
obj1 = spawner.spawn(self.prot1['prototype_key'])
|
||||||
|
self.assertEqual(list(protlib.search_objects_with_prototype("testprototype")), obj1)
|
||||||
|
self.assertEqual([o.key for o in spawner.spawn(
|
||||||
|
_PROTPARENTS["GOBLIN"], _PROTPARENTS["GOBLIN_ARCHWIZARD"],
|
||||||
|
prototype_parents=_PROTPARENTS)], ['goblin grunt', 'goblin archwizard'])
|
||||||
|
|
||||||
|
|
||||||
class TestUtils(EvenniaTest):
|
class TestUtils(EvenniaTest):
|
||||||
|
|
||||||
|
|
@ -245,6 +253,7 @@ class TestProtLib(EvenniaTest):
|
||||||
super(TestProtLib, self).setUp()
|
super(TestProtLib, self).setUp()
|
||||||
self.obj1.attributes.add("testattr", "testval")
|
self.obj1.attributes.add("testattr", "testval")
|
||||||
self.prot = spawner.prototype_from_object(self.obj1)
|
self.prot = spawner.prototype_from_object(self.obj1)
|
||||||
|
|
||||||
|
|
||||||
def test_prototype_to_str(self):
|
def test_prototype_to_str(self):
|
||||||
prstr = protlib.prototype_to_str(self.prot)
|
prstr = protlib.prototype_to_str(self.prot)
|
||||||
|
|
@ -253,6 +262,22 @@ class TestProtLib(EvenniaTest):
|
||||||
def test_check_permission(self):
|
def test_check_permission(self):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
def test_save_prototype(self):
|
||||||
|
result = protlib.save_prototype(self.prot)
|
||||||
|
self.assertEqual(result, self.prot)
|
||||||
|
# faulty
|
||||||
|
self.prot['prototype_key'] = None
|
||||||
|
self.assertRaises(protlib.ValidationError, protlib.save_prototype, self.prot)
|
||||||
|
|
||||||
|
def test_search_prototype(self):
|
||||||
|
protlib.save_prototype(self.prot)
|
||||||
|
match = protlib.search_prototype("NotFound")
|
||||||
|
self.assertFalse(match)
|
||||||
|
match = protlib.search_prototype()
|
||||||
|
self.assertTrue(match)
|
||||||
|
match = protlib.search_prototype(self.prot['prototype_key'])
|
||||||
|
self.assertEqual(match, [self.prot])
|
||||||
|
|
||||||
|
|
||||||
@override_settings(PROT_FUNC_MODULES=['evennia.prototypes.protfuncs'], CLIENT_DEFAULT_WIDTH=20)
|
@override_settings(PROT_FUNC_MODULES=['evennia.prototypes.protfuncs'], CLIENT_DEFAULT_WIDTH=20)
|
||||||
class TestProtFuncs(EvenniaTest):
|
class TestProtFuncs(EvenniaTest):
|
||||||
|
|
@ -424,7 +449,7 @@ class TestPrototypeStorage(EvenniaTest):
|
||||||
def test_prototype_storage(self):
|
def test_prototype_storage(self):
|
||||||
|
|
||||||
# from evennia import set_trace;set_trace(term_size=(180, 50))
|
# from evennia import set_trace;set_trace(term_size=(180, 50))
|
||||||
prot1 = protlib.create_prototype(**self.prot1)
|
prot1 = protlib.create_prototype(self.prot1)
|
||||||
|
|
||||||
self.assertTrue(bool(prot1))
|
self.assertTrue(bool(prot1))
|
||||||
self.assertEqual(prot1, self.prot1)
|
self.assertEqual(prot1, self.prot1)
|
||||||
|
|
@ -436,7 +461,7 @@ class TestPrototypeStorage(EvenniaTest):
|
||||||
protlib.DbPrototype.objects.get_by_tag(
|
protlib.DbPrototype.objects.get_by_tag(
|
||||||
"foo1", _PROTOTYPE_TAG_META_CATEGORY)[0].db.prototype, prot1)
|
"foo1", _PROTOTYPE_TAG_META_CATEGORY)[0].db.prototype, prot1)
|
||||||
|
|
||||||
prot2 = protlib.create_prototype(**self.prot2)
|
prot2 = protlib.create_prototype(self.prot2)
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
[pobj.db.prototype
|
[pobj.db.prototype
|
||||||
for pobj in protlib.DbPrototype.objects.get_by_tag(
|
for pobj in protlib.DbPrototype.objects.get_by_tag(
|
||||||
|
|
@ -445,7 +470,7 @@ class TestPrototypeStorage(EvenniaTest):
|
||||||
|
|
||||||
# add to existing prototype
|
# add to existing prototype
|
||||||
prot1b = protlib.create_prototype(
|
prot1b = protlib.create_prototype(
|
||||||
prototype_key='testprototype1', foo='bar', prototype_tags=['foo2'])
|
{"prototype_key": 'testprototype1', "foo": 'bar', "prototype_tags": ['foo2']})
|
||||||
|
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
[pobj.db.prototype
|
[pobj.db.prototype
|
||||||
|
|
@ -457,7 +482,7 @@ class TestPrototypeStorage(EvenniaTest):
|
||||||
self.assertNotEqual(list(protlib.search_prototype("testprototype1")), [prot1])
|
self.assertNotEqual(list(protlib.search_prototype("testprototype1")), [prot1])
|
||||||
self.assertEqual(list(protlib.search_prototype("testprototype1")), [prot1b])
|
self.assertEqual(list(protlib.search_prototype("testprototype1")), [prot1b])
|
||||||
|
|
||||||
prot3 = protlib.create_prototype(**self.prot3)
|
prot3 = protlib.create_prototype(self.prot3)
|
||||||
|
|
||||||
# partial match
|
# partial match
|
||||||
with mock.patch("evennia.prototypes.prototypes._MODULE_PROTOTYPES", {}):
|
with mock.patch("evennia.prototypes.prototypes._MODULE_PROTOTYPES", {}):
|
||||||
|
|
@ -608,7 +633,7 @@ class TestMenuModule(EvenniaTest):
|
||||||
self.assertEqual(olc_menus._display_tag(olc_menus._get_menu_prototype(caller)['tags'][0]), Something)
|
self.assertEqual(olc_menus._display_tag(olc_menus._get_menu_prototype(caller)['tags'][0]), Something)
|
||||||
self.assertEqual(olc_menus._caller_tags(caller), ["foo2", "foo3"])
|
self.assertEqual(olc_menus._caller_tags(caller), ["foo2", "foo3"])
|
||||||
|
|
||||||
protlib.save_prototype(**self.test_prot)
|
protlib.save_prototype(self.test_prot)
|
||||||
|
|
||||||
# locks helpers
|
# locks helpers
|
||||||
self.assertEqual(olc_menus._lock_add(caller, "foo:false()"), "Added lock 'foo:false()'.")
|
self.assertEqual(olc_menus._lock_add(caller, "foo:false()"), "Added lock 'foo:false()'.")
|
||||||
|
|
|
||||||
|
|
@ -13,6 +13,7 @@ Run the script with the -h flag to see usage information.
|
||||||
|
|
||||||
import os
|
import os
|
||||||
import sys
|
import sys
|
||||||
|
import re
|
||||||
import signal
|
import signal
|
||||||
import shutil
|
import shutil
|
||||||
import importlib
|
import importlib
|
||||||
|
|
@ -25,6 +26,7 @@ from subprocess import Popen, check_output, call, CalledProcessError, STDOUT
|
||||||
from twisted.protocols import amp
|
from twisted.protocols import amp
|
||||||
from twisted.internet import reactor, endpoints
|
from twisted.internet import reactor, endpoints
|
||||||
import django
|
import django
|
||||||
|
from django.core.management import execute_from_command_line
|
||||||
|
|
||||||
# Signal processing
|
# Signal processing
|
||||||
SIG = signal.SIGINT
|
SIG = signal.SIGINT
|
||||||
|
|
@ -393,6 +395,7 @@ ERROR_DJANGO_MIN = \
|
||||||
ERROR: Django {dversion} found. Evennia requires version {django_min}
|
ERROR: Django {dversion} found. Evennia requires version {django_min}
|
||||||
or higher.
|
or higher.
|
||||||
|
|
||||||
|
TE_TEST
|
||||||
If you are using a virtualenv, use the command `pip install --upgrade -e evennia` where
|
If you are using a virtualenv, use the command `pip install --upgrade -e evennia` where
|
||||||
`evennia` is the folder to where you cloned the Evennia library. If not
|
`evennia` is the folder to where you cloned the Evennia library. If not
|
||||||
in a virtualenv you can install django with for example `pip install --upgrade django`
|
in a virtualenv you can install django with for example `pip install --upgrade django`
|
||||||
|
|
@ -428,16 +431,15 @@ NOTE_KEYBOARDINTERRUPT = \
|
||||||
NOTE_TEST_DEFAULT = \
|
NOTE_TEST_DEFAULT = \
|
||||||
"""
|
"""
|
||||||
TESTING: Using Evennia's default settings file (evennia.settings_default).
|
TESTING: Using Evennia's default settings file (evennia.settings_default).
|
||||||
(use 'evennia --settings settings.py test .' to run tests on the game dir)
|
(use 'evennia test --settings settings.py .' to run only your custom game tests)
|
||||||
"""
|
"""
|
||||||
|
|
||||||
NOTE_TEST_CUSTOM = \
|
NOTE_TEST_CUSTOM = \
|
||||||
"""
|
"""
|
||||||
TESTING: Using specified settings file '{settings_dotpath}'.
|
TESTING: Using specified settings file '{settings_dotpath}'.
|
||||||
|
|
||||||
(Obs: Evennia's full test suite may not pass if the settings are very
|
OBS: Evennia's full test suite may not pass if the settings are very
|
||||||
different from the default. Use 'test .' as arguments to run only tests
|
different from the default (use 'evennia test evennia' to run core tests)
|
||||||
on the game dir.)
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
PROCESS_ERROR = \
|
PROCESS_ERROR = \
|
||||||
|
|
@ -519,7 +521,7 @@ def _print_info(portal_info_dict, server_info_dict):
|
||||||
out = {}
|
out = {}
|
||||||
for key, value in dct.items():
|
for key, value in dct.items():
|
||||||
if isinstance(value, list):
|
if isinstance(value, list):
|
||||||
value = "\n{}".format(ind).join(value)
|
value = "\n{}".format(ind).join(str(val) for val in value)
|
||||||
out[key] = value
|
out[key] = value
|
||||||
return out
|
return out
|
||||||
|
|
||||||
|
|
@ -686,13 +688,14 @@ def send_instruction(operation, arguments, callback=None, errback=None):
|
||||||
|
|
||||||
if AMP_CONNECTION:
|
if AMP_CONNECTION:
|
||||||
# already connected - send right away
|
# already connected - send right away
|
||||||
_send()
|
return _send()
|
||||||
else:
|
else:
|
||||||
# we must connect first, send once connected
|
# we must connect first, send once connected
|
||||||
point = endpoints.TCP4ClientEndpoint(reactor, AMP_HOST, AMP_PORT)
|
point = endpoints.TCP4ClientEndpoint(reactor, AMP_HOST, AMP_PORT)
|
||||||
deferred = endpoints.connectProtocol(point, AMPLauncherProtocol())
|
deferred = endpoints.connectProtocol(point, AMPLauncherProtocol())
|
||||||
deferred.addCallbacks(_on_connect, _on_connect_fail)
|
deferred.addCallbacks(_on_connect, _on_connect_fail)
|
||||||
REACTOR_RUN = True
|
REACTOR_RUN = True
|
||||||
|
return deferred
|
||||||
|
|
||||||
|
|
||||||
def query_status(callback=None):
|
def query_status(callback=None):
|
||||||
|
|
@ -2116,7 +2119,7 @@ def main():
|
||||||
else:
|
else:
|
||||||
kill(SERVER_PIDFILE, 'Server')
|
kill(SERVER_PIDFILE, 'Server')
|
||||||
elif option != "noop":
|
elif option != "noop":
|
||||||
# pass-through to django manager
|
# pass-through to django manager, but set things up first
|
||||||
check_db = False
|
check_db = False
|
||||||
need_gamedir = True
|
need_gamedir = True
|
||||||
# some commands don't require the presence of a game directory to work
|
# some commands don't require the presence of a game directory to work
|
||||||
|
|
@ -2136,31 +2139,16 @@ def main():
|
||||||
|
|
||||||
init_game_directory(CURRENT_DIR, check_db=check_db, need_gamedir=need_gamedir)
|
init_game_directory(CURRENT_DIR, check_db=check_db, need_gamedir=need_gamedir)
|
||||||
|
|
||||||
# pass on to the manager
|
if option == "migrate":
|
||||||
args = [option]
|
# we have to launch migrate within the program to make sure migrations
|
||||||
kwargs = {}
|
# run within the scope of the launcher (otherwise missing a db will cause errors)
|
||||||
if unknown_args:
|
django.core.management.call_command(*([option] + unknown_args))
|
||||||
for arg in unknown_args:
|
else:
|
||||||
if arg.startswith("--"):
|
# pass on to the core django manager - re-parse the entire input line
|
||||||
print("arg:", arg)
|
# but keep 'evennia' as the name instead of django-admin. This is
|
||||||
if "=" in arg:
|
# an exit condition.
|
||||||
arg, value = [p.strip() for p in arg.split("=", 1)]
|
sys.argv[0] = re.sub(r'(-script\.pyw?|\.exe)?$', '', sys.argv[0])
|
||||||
else:
|
sys.exit(execute_from_command_line())
|
||||||
value = True
|
|
||||||
kwargs[arg.lstrip("--")] = value
|
|
||||||
else:
|
|
||||||
args.append(arg)
|
|
||||||
|
|
||||||
# makemessages needs a special syntax to not conflict with the -l option
|
|
||||||
if len(args) > 1 and args[0] == "makemessages":
|
|
||||||
args.insert(1, "-l")
|
|
||||||
|
|
||||||
try:
|
|
||||||
django.core.management.call_command(*args, **kwargs)
|
|
||||||
except django.core.management.base.CommandError as exc:
|
|
||||||
args = ", ".join(args)
|
|
||||||
kwargs = ", ".join(["--%s" % kw for kw in kwargs])
|
|
||||||
print(ERROR_INPUT.format(traceback=exc, args=args, kwargs=kwargs))
|
|
||||||
|
|
||||||
elif not args.tail_log:
|
elif not args.tail_log:
|
||||||
# no input; print evennia info (don't pring if we're tailing log)
|
# no input; print evennia info (don't pring if we're tailing log)
|
||||||
|
|
|
||||||
|
|
@ -282,6 +282,8 @@ class AMPMultiConnectionProtocol(amp.AMP):
|
||||||
self.send_mode = True
|
self.send_mode = True
|
||||||
self.send_task = None
|
self.send_task = None
|
||||||
self.multibatches = 0
|
self.multibatches = 0
|
||||||
|
# later twisted amp has its own __init__
|
||||||
|
super(AMPMultiConnectionProtocol, self).__init__(*args, **kwargs)
|
||||||
|
|
||||||
def dataReceived(self, data):
|
def dataReceived(self, data):
|
||||||
"""
|
"""
|
||||||
|
|
|
||||||
|
|
@ -105,7 +105,8 @@ class Portal(object):
|
||||||
sys.path.append('.')
|
sys.path.append('.')
|
||||||
|
|
||||||
# create a store of services
|
# create a store of services
|
||||||
self.services = service.IServiceCollection(application)
|
self.services = service.MultiService()
|
||||||
|
self.services.setServiceParent(application)
|
||||||
self.amp_protocol = None # set by amp factory
|
self.amp_protocol = None # set by amp factory
|
||||||
self.sessions = PORTAL_SESSIONS
|
self.sessions = PORTAL_SESSIONS
|
||||||
self.sessions.portal = self
|
self.sessions.portal = self
|
||||||
|
|
|
||||||
|
|
@ -103,6 +103,7 @@ class TestMemPlot(TestCase):
|
||||||
@patch.object(memplot, "os")
|
@patch.object(memplot, "os")
|
||||||
@patch.object(memplot, "open", new_callable=mock_open, create=True)
|
@patch.object(memplot, "open", new_callable=mock_open, create=True)
|
||||||
@patch.object(memplot, "time")
|
@patch.object(memplot, "time")
|
||||||
|
@patch("evennia.utils.idmapper.models.SharedMemoryModel.flush_from_cache", new=Mock())
|
||||||
def test_memplot(self, mock_time, mocked_open, mocked_os, mocked_idmapper):
|
def test_memplot(self, mock_time, mocked_open, mocked_os, mocked_idmapper):
|
||||||
if isinstance(memplot, Mock):
|
if isinstance(memplot, Mock):
|
||||||
return
|
return
|
||||||
|
|
|
||||||
|
|
@ -143,8 +143,6 @@ def _server_maintenance():
|
||||||
session.account.access(session.account, "noidletimeout", default=False):
|
session.account.access(session.account, "noidletimeout", default=False):
|
||||||
SESSIONS.disconnect(session, reason=reason)
|
SESSIONS.disconnect(session, reason=reason)
|
||||||
|
|
||||||
maintenance_task = LoopingCall(_server_maintenance)
|
|
||||||
maintenance_task.start(60, now=True) # call every minute
|
|
||||||
|
|
||||||
#------------------------------------------------------------
|
#------------------------------------------------------------
|
||||||
# Evennia Main Server object
|
# Evennia Main Server object
|
||||||
|
|
@ -199,6 +197,7 @@ class Evennia(object):
|
||||||
reactor.callLater(1, d.callback, None)
|
reactor.callLater(1, d.callback, None)
|
||||||
reactor.sigInt = _wrap_sigint_handler
|
reactor.sigInt = _wrap_sigint_handler
|
||||||
|
|
||||||
|
|
||||||
# Server startup methods
|
# Server startup methods
|
||||||
|
|
||||||
def sqlite3_prep(self):
|
def sqlite3_prep(self):
|
||||||
|
|
@ -303,6 +302,10 @@ class Evennia(object):
|
||||||
"""
|
"""
|
||||||
from evennia.objects.models import ObjectDB
|
from evennia.objects.models import ObjectDB
|
||||||
|
|
||||||
|
# start server time and maintenance task
|
||||||
|
self.maintenance_task = LoopingCall(_server_maintenance)
|
||||||
|
self.maintenance_task.start(60, now=True) # call every minute
|
||||||
|
|
||||||
# update eventual changed defaults
|
# update eventual changed defaults
|
||||||
self.update_defaults()
|
self.update_defaults()
|
||||||
|
|
||||||
|
|
@ -322,6 +325,7 @@ class Evennia(object):
|
||||||
# clear eventual lingering session storages
|
# clear eventual lingering session storages
|
||||||
ObjectDB.objects.clear_all_sessids()
|
ObjectDB.objects.clear_all_sessids()
|
||||||
logger.log_msg("Evennia Server successfully started.")
|
logger.log_msg("Evennia Server successfully started.")
|
||||||
|
|
||||||
# always call this regardless of start type
|
# always call this regardless of start type
|
||||||
self.at_server_start()
|
self.at_server_start()
|
||||||
|
|
||||||
|
|
|
||||||
126
evennia/server/tests/test_amp_connection.py
Normal file
126
evennia/server/tests/test_amp_connection.py
Normal file
|
|
@ -0,0 +1,126 @@
|
||||||
|
"""
|
||||||
|
Test AMP client
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
import pickle
|
||||||
|
from unittest import TestCase
|
||||||
|
from mock import MagicMock, patch
|
||||||
|
from twisted.trial.unittest import TestCase as TwistedTestCase
|
||||||
|
from evennia.server import amp_client
|
||||||
|
from evennia.server.portal import amp_server
|
||||||
|
from evennia.server.portal import amp
|
||||||
|
from evennia.server import server
|
||||||
|
from evennia.server.portal import portal
|
||||||
|
from evennia.server import serversession, session
|
||||||
|
from evennia.utils import create
|
||||||
|
|
||||||
|
from twisted.internet.base import DelayedCall
|
||||||
|
DelayedCall.debug = True
|
||||||
|
|
||||||
|
class _TestAMP(TwistedTestCase):
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
super(_TestAMP, self).setUp()
|
||||||
|
with patch("evennia.server.initial_setup.get_god_account") as mockgod:
|
||||||
|
self.account = create.account("TestAMPAccount", "test@test.com", "testpassword")
|
||||||
|
mockgod.return_value = self.account
|
||||||
|
self.server = server.Evennia(MagicMock())
|
||||||
|
self.server.sessions.data_in = MagicMock()
|
||||||
|
self.server.sessions.data_out = MagicMock()
|
||||||
|
self.amp_client_factory = amp_client.AMPClientFactory(self.server)
|
||||||
|
self.amp_client = self.amp_client_factory.buildProtocol("127.0.0.1")
|
||||||
|
self.session = MagicMock() # serversession.ServerSession()
|
||||||
|
self.session.sessid = 1
|
||||||
|
self.server.sessions[1] = self.session
|
||||||
|
|
||||||
|
self.portal = portal.Portal(MagicMock())
|
||||||
|
self.portalsession = session.Session()
|
||||||
|
self.portalsession.sessid = 1
|
||||||
|
self.portal.sessions[1] = self.portalsession
|
||||||
|
self.portal.sessions.data_in = MagicMock()
|
||||||
|
self.portal.sessions.data_out = MagicMock()
|
||||||
|
self.amp_server_factory = amp_server.AMPServerFactory(self.portal)
|
||||||
|
self.amp_server = self.amp_server_factory.buildProtocol("127.0.0.1")
|
||||||
|
|
||||||
|
def tearDown(self):
|
||||||
|
self.account.delete()
|
||||||
|
super(_TestAMP, self).tearDown()
|
||||||
|
|
||||||
|
def _connect_client(self, mocktransport):
|
||||||
|
"Setup client to send data for testing"
|
||||||
|
mocktransport.write = MagicMock()
|
||||||
|
self.amp_client.makeConnection(mocktransport)
|
||||||
|
mocktransport.write.reset_mock()
|
||||||
|
|
||||||
|
def _connect_server(self, mocktransport):
|
||||||
|
"Setup server to send data for testing"
|
||||||
|
mocktransport.write = MagicMock()
|
||||||
|
self.amp_server.makeConnection(mocktransport)
|
||||||
|
mocktransport.write.reset_mock()
|
||||||
|
|
||||||
|
def _catch_wire_read(self, mocktransport):
|
||||||
|
"Parse what was supposed to be sent over the wire"
|
||||||
|
arg_list = mocktransport.write.call_args_list
|
||||||
|
|
||||||
|
all_sent = []
|
||||||
|
for i, cll in enumerate(arg_list):
|
||||||
|
args, kwargs = cll
|
||||||
|
raw_inp = args[0]
|
||||||
|
all_sent.append(raw_inp)
|
||||||
|
|
||||||
|
return all_sent
|
||||||
|
|
||||||
|
|
||||||
|
@patch("evennia.server.server.LoopingCall", MagicMock())
|
||||||
|
@patch("evennia.server.portal.amp.amp.BinaryBoxProtocol.transport")
|
||||||
|
class TestAMPClientSend(_TestAMP):
|
||||||
|
"""Test amp client sending data"""
|
||||||
|
|
||||||
|
def test_msgserver2portal(self, mocktransport):
|
||||||
|
self._connect_client(mocktransport)
|
||||||
|
self.amp_client.send_MsgServer2Portal(self.session, text={"foo": "bar"})
|
||||||
|
wire_data = self._catch_wire_read(mocktransport)[0]
|
||||||
|
|
||||||
|
self._connect_server(mocktransport)
|
||||||
|
self.amp_server.dataReceived(wire_data)
|
||||||
|
self.portal.sessions.data_out.assert_called_with(self.portalsession, text={"foo": "bar"})
|
||||||
|
|
||||||
|
def test_adminserver2portal(self, mocktransport):
|
||||||
|
self._connect_client(mocktransport)
|
||||||
|
|
||||||
|
self.amp_client.send_AdminServer2Portal(self.session,
|
||||||
|
operation=amp.PSYNC,
|
||||||
|
info_dict={}, spid=None)
|
||||||
|
wire_data = self._catch_wire_read(mocktransport)[0]
|
||||||
|
|
||||||
|
self._connect_server(mocktransport)
|
||||||
|
self.amp_server.data_in = MagicMock()
|
||||||
|
self.amp_server.dataReceived(wire_data)
|
||||||
|
self.amp_server.data_in.assert_called()
|
||||||
|
|
||||||
|
|
||||||
|
@patch("evennia.server.portal.amp.amp.BinaryBoxProtocol.transport")
|
||||||
|
class TestAMPClientRecv(_TestAMP):
|
||||||
|
"""Test amp client sending data"""
|
||||||
|
|
||||||
|
def test_msgportal2server(self, mocktransport):
|
||||||
|
self._connect_server(mocktransport)
|
||||||
|
self.amp_server.send_MsgPortal2Server(self.session, text={"foo": "bar"})
|
||||||
|
wire_data = self._catch_wire_read(mocktransport)[0]
|
||||||
|
|
||||||
|
self._connect_client(mocktransport)
|
||||||
|
self.amp_client.dataReceived(wire_data)
|
||||||
|
self.server.sessions.data_in.assert_called_with(self.session, text={"foo": "bar"})
|
||||||
|
|
||||||
|
def test_adminportal2server(self, mocktransport):
|
||||||
|
self._connect_server(mocktransport)
|
||||||
|
|
||||||
|
self.amp_server.send_AdminPortal2Server(self.session,
|
||||||
|
operation=amp.PDISCONNALL)
|
||||||
|
wire_data = self._catch_wire_read(mocktransport)[0]
|
||||||
|
|
||||||
|
self._connect_client(mocktransport)
|
||||||
|
self.server.sessions.portal_disconnect_all = MagicMock()
|
||||||
|
self.amp_client.dataReceived(wire_data)
|
||||||
|
self.server.sessions.portal_disconnect_all.assert_called()
|
||||||
197
evennia/server/tests/test_launcher.py
Normal file
197
evennia/server/tests/test_launcher.py
Normal file
|
|
@ -0,0 +1,197 @@
|
||||||
|
"""
|
||||||
|
Test the evennia launcher.
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
import os
|
||||||
|
import pickle
|
||||||
|
from anything import Something
|
||||||
|
from mock import patch, MagicMock, create_autospec
|
||||||
|
from twisted.internet import reactor
|
||||||
|
from twisted.trial.unittest import TestCase as TwistedTestCase
|
||||||
|
from evennia.server import evennia_launcher
|
||||||
|
from evennia.server.portal import amp
|
||||||
|
|
||||||
|
from twisted.internet.base import DelayedCall
|
||||||
|
DelayedCall.debug = True
|
||||||
|
|
||||||
|
@patch("evennia.server.evennia_launcher.Popen", new=MagicMock())
|
||||||
|
class TestLauncher(TwistedTestCase):
|
||||||
|
|
||||||
|
def test_is_windows(self):
|
||||||
|
self.assertEqual(evennia_launcher._is_windows(), os.name == 'nt')
|
||||||
|
|
||||||
|
def test_file_compact(self):
|
||||||
|
self.assertEqual(evennia_launcher._file_names_compact(
|
||||||
|
"foo/bar/test1", "foo/bar/test2"),
|
||||||
|
"foo/bar/test1 and test2")
|
||||||
|
|
||||||
|
self.assertEqual(evennia_launcher._file_names_compact(
|
||||||
|
"foo/test1", "foo/bar/test2"),
|
||||||
|
"foo/test1 and foo/bar/test2")
|
||||||
|
|
||||||
|
@patch("evennia.server.evennia_launcher.print")
|
||||||
|
def test_print_info(self, mockprint):
|
||||||
|
portal_dict = {
|
||||||
|
"servername": "testserver",
|
||||||
|
"version": "1",
|
||||||
|
"telnet": 1234,
|
||||||
|
"telnet_ssl": [1234, 2345],
|
||||||
|
"ssh": 1234,
|
||||||
|
"webserver_proxy": 1234,
|
||||||
|
"webclient": 1234,
|
||||||
|
"webserver_internal": 1234,
|
||||||
|
"amp": 1234
|
||||||
|
}
|
||||||
|
server_dict = {
|
||||||
|
"servername": "testserver",
|
||||||
|
"version": "1",
|
||||||
|
"webserver": [1234, 1234],
|
||||||
|
"amp": 1234,
|
||||||
|
"irc_rss": "irc.test",
|
||||||
|
"info": "testing mode",
|
||||||
|
"errors": ""
|
||||||
|
}
|
||||||
|
|
||||||
|
evennia_launcher._print_info(portal_dict, server_dict)
|
||||||
|
mockprint.assert_called()
|
||||||
|
|
||||||
|
def test_parse_status(self):
|
||||||
|
response = {"status": pickle.dumps(("teststring",))}
|
||||||
|
result = evennia_launcher._parse_status(response)
|
||||||
|
self.assertEqual(result, ("teststring",))
|
||||||
|
|
||||||
|
@patch("evennia.server.evennia_launcher.os.name", new="posix")
|
||||||
|
def test_get_twisted_cmdline(self):
|
||||||
|
pcmd, scmd = evennia_launcher._get_twistd_cmdline(False, False)
|
||||||
|
self.assertTrue("portal.py" in pcmd[1])
|
||||||
|
self.assertTrue("--pidfile" in pcmd[2])
|
||||||
|
self.assertTrue("server.py" in scmd[1])
|
||||||
|
self.assertTrue("--pidfile" in scmd[2])
|
||||||
|
|
||||||
|
pcmd, scmd = evennia_launcher._get_twistd_cmdline(True, True)
|
||||||
|
self.assertTrue("portal.py" in pcmd[1])
|
||||||
|
self.assertTrue("--pidfile" in pcmd[2])
|
||||||
|
self.assertTrue("--profiler=cprofile" in pcmd[4], "actual: {}".format(pcmd))
|
||||||
|
self.assertTrue("--profile=" in pcmd[5])
|
||||||
|
self.assertTrue("server.py" in scmd[1])
|
||||||
|
self.assertTrue("--pidfile" in scmd[2])
|
||||||
|
self.assertTrue("--pidfile" in scmd[2])
|
||||||
|
self.assertTrue("--profiler=cprofile" in scmd[4], "actual: {}".format(scmd))
|
||||||
|
self.assertTrue("--profile=" in scmd[5])
|
||||||
|
|
||||||
|
@patch("evennia.server.evennia_launcher.os.name", new="nt")
|
||||||
|
def test_get_twisted_cmdline_nt(self):
|
||||||
|
pcmd, scmd = evennia_launcher._get_twistd_cmdline(False, False)
|
||||||
|
self.assertTrue(len(pcmd) == 2, "actual: {}".format(pcmd))
|
||||||
|
self.assertTrue(len(scmd) == 2, "actual: {}".format(scmd))
|
||||||
|
|
||||||
|
@patch("evennia.server.evennia_launcher.reactor.stop")
|
||||||
|
def test_reactor_stop(self, mockstop):
|
||||||
|
evennia_launcher._reactor_stop()
|
||||||
|
mockstop.assert_called()
|
||||||
|
|
||||||
|
def _catch_wire_read(self, mocktransport):
|
||||||
|
"Parse what was supposed to be sent over the wire"
|
||||||
|
arg_list = mocktransport.write.call_args_list
|
||||||
|
|
||||||
|
all_sent = []
|
||||||
|
for i, cll in enumerate(arg_list):
|
||||||
|
args, kwargs = cll
|
||||||
|
raw_inp = args[0]
|
||||||
|
all_sent.append(raw_inp)
|
||||||
|
|
||||||
|
return all_sent
|
||||||
|
|
||||||
|
# @patch("evennia.server.portal.amp.amp.BinaryBoxProtocol.transport")
|
||||||
|
# def test_send_instruction_pstatus(self, mocktransport):
|
||||||
|
|
||||||
|
# deferred = evennia_launcher.send_instruction(
|
||||||
|
# evennia_launcher.PSTATUS,
|
||||||
|
# (),
|
||||||
|
# callback=MagicMock(),
|
||||||
|
# errback=MagicMock())
|
||||||
|
|
||||||
|
# on_wire = self._catch_wire_read(mocktransport)
|
||||||
|
# self.assertEqual(on_wire, "")
|
||||||
|
|
||||||
|
# return deferred
|
||||||
|
|
||||||
|
def _msend_status_ok(operation, arguments, callback=None, errback=None):
|
||||||
|
callback({"status": pickle.dumps((True, True, 2, 24, "info1", "info2"))})
|
||||||
|
|
||||||
|
def _msend_status_err(operation, arguments, callback=None, errback=None):
|
||||||
|
errback({"status": pickle.dumps((False, False, 3, 25, "info3", "info4"))})
|
||||||
|
|
||||||
|
@patch("evennia.server.evennia_launcher.send_instruction", _msend_status_ok)
|
||||||
|
@patch("evennia.server.evennia_launcher.NO_REACTOR_STOP", True)
|
||||||
|
@patch("evennia.server.evennia_launcher.get_pid", MagicMock(return_value=100))
|
||||||
|
@patch("evennia.server.evennia_launcher.print")
|
||||||
|
def test_query_status_run(self, mprint):
|
||||||
|
evennia_launcher.query_status()
|
||||||
|
mprint.assert_called_with('Portal: RUNNING (pid 100)\nServer: RUNNING (pid 100)')
|
||||||
|
|
||||||
|
@patch("evennia.server.evennia_launcher.send_instruction", _msend_status_err)
|
||||||
|
@patch("evennia.server.evennia_launcher.NO_REACTOR_STOP", True)
|
||||||
|
@patch("evennia.server.evennia_launcher.print")
|
||||||
|
def test_query_status_not_run(self, mprint):
|
||||||
|
evennia_launcher.query_status()
|
||||||
|
mprint.assert_called_with('Portal: NOT RUNNING\nServer: NOT RUNNING')
|
||||||
|
|
||||||
|
@patch("evennia.server.evennia_launcher.send_instruction", _msend_status_ok)
|
||||||
|
@patch("evennia.server.evennia_launcher.NO_REACTOR_STOP", True)
|
||||||
|
def test_query_status_callback(self):
|
||||||
|
mprint = MagicMock()
|
||||||
|
|
||||||
|
def testcall(response):
|
||||||
|
resp = pickle.loads(response['status'])
|
||||||
|
mprint(resp)
|
||||||
|
|
||||||
|
evennia_launcher.query_status(callback=testcall)
|
||||||
|
mprint.assert_called_with((True, True, 2, 24, "info1", "info2"))
|
||||||
|
|
||||||
|
@patch("evennia.server.evennia_launcher.AMP_CONNECTION")
|
||||||
|
@patch("evennia.server.evennia_launcher.print")
|
||||||
|
def test_wait_for_status_reply(self, mprint, aconn):
|
||||||
|
aconn.wait_for_status = MagicMock()
|
||||||
|
|
||||||
|
def test():
|
||||||
|
pass
|
||||||
|
|
||||||
|
evennia_launcher.wait_for_status_reply(test)
|
||||||
|
aconn.wait_for_status.assert_called_with(test)
|
||||||
|
|
||||||
|
@patch("evennia.server.evennia_launcher.AMP_CONNECTION", None)
|
||||||
|
@patch("evennia.server.evennia_launcher.print")
|
||||||
|
def test_wait_for_status_reply_fail(self, mprint):
|
||||||
|
evennia_launcher.wait_for_status_reply(None)
|
||||||
|
mprint.assert_called_with("No Evennia connection established.")
|
||||||
|
|
||||||
|
@patch("evennia.server.evennia_launcher.send_instruction", _msend_status_ok)
|
||||||
|
@patch("evennia.server.evennia_launcher.reactor.callLater")
|
||||||
|
def test_wait_for_status(self, mcalllater):
|
||||||
|
mcall = MagicMock()
|
||||||
|
merr = MagicMock()
|
||||||
|
evennia_launcher.wait_for_status(
|
||||||
|
portal_running=True,
|
||||||
|
server_running=True,
|
||||||
|
callback=mcall,
|
||||||
|
errback=merr)
|
||||||
|
|
||||||
|
mcall.assert_called_with(True, True)
|
||||||
|
merr.assert_not_called()
|
||||||
|
|
||||||
|
@patch("evennia.server.evennia_launcher.send_instruction", _msend_status_err)
|
||||||
|
@patch("evennia.server.evennia_launcher.reactor.callLater")
|
||||||
|
def test_wait_for_status_fail(self, mcalllater):
|
||||||
|
mcall = MagicMock()
|
||||||
|
merr = MagicMock()
|
||||||
|
evennia_launcher.wait_for_status(
|
||||||
|
portal_running=True,
|
||||||
|
server_running=True,
|
||||||
|
callback=mcall,
|
||||||
|
errback=merr)
|
||||||
|
|
||||||
|
mcall.assert_not_called()
|
||||||
|
merr.assert_not_called()
|
||||||
|
mcalllater.assert_called()
|
||||||
242
evennia/server/tests/test_server.py
Normal file
242
evennia/server/tests/test_server.py
Normal file
|
|
@ -0,0 +1,242 @@
|
||||||
|
"""
|
||||||
|
Test the main server component
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
from unittest import TestCase
|
||||||
|
from mock import MagicMock, patch, DEFAULT, call
|
||||||
|
from django.test import override_settings
|
||||||
|
from evennia.utils.test_resources import unload_module
|
||||||
|
|
||||||
|
|
||||||
|
@patch("evennia.server.server.LoopingCall", new=MagicMock())
|
||||||
|
class TestServer(TestCase):
|
||||||
|
"""
|
||||||
|
Test server module.
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
from evennia.server import server
|
||||||
|
self.server = server
|
||||||
|
|
||||||
|
def test__server_maintenance_reset(self):
|
||||||
|
with patch.multiple("evennia.server.server",
|
||||||
|
LoopingCall=DEFAULT,
|
||||||
|
Evennia=DEFAULT,
|
||||||
|
_FLUSH_CACHE=DEFAULT,
|
||||||
|
connection=DEFAULT,
|
||||||
|
_IDMAPPER_CACHE_MAXSIZE=1000,
|
||||||
|
_MAINTENANCE_COUNT=0,
|
||||||
|
ServerConfig=DEFAULT) as mocks:
|
||||||
|
mocks['connection'].close = MagicMock()
|
||||||
|
mocks['ServerConfig'].objects.conf = MagicMock(return_value=456)
|
||||||
|
|
||||||
|
# flush cache
|
||||||
|
self.server._server_maintenance()
|
||||||
|
mocks['ServerConfig'].objects.conf.assert_called_with('runtime', 456)
|
||||||
|
|
||||||
|
def test__server_maintenance_flush(self):
|
||||||
|
with patch.multiple("evennia.server.server",
|
||||||
|
LoopingCall=DEFAULT,
|
||||||
|
Evennia=DEFAULT,
|
||||||
|
_FLUSH_CACHE=DEFAULT,
|
||||||
|
connection=DEFAULT,
|
||||||
|
_IDMAPPER_CACHE_MAXSIZE=1000,
|
||||||
|
_MAINTENANCE_COUNT=600 - 1,
|
||||||
|
ServerConfig=DEFAULT) as mocks:
|
||||||
|
mocks['connection'].close = MagicMock()
|
||||||
|
mocks['ServerConfig'].objects.conf = MagicMock(return_value=100)
|
||||||
|
|
||||||
|
# flush cache
|
||||||
|
self.server._server_maintenance()
|
||||||
|
mocks['_FLUSH_CACHE'].assert_called_with(1000)
|
||||||
|
|
||||||
|
def test__server_maintenance_validate_scripts(self):
|
||||||
|
with patch.multiple("evennia.server.server",
|
||||||
|
LoopingCall=DEFAULT,
|
||||||
|
Evennia=DEFAULT,
|
||||||
|
_FLUSH_CACHE=DEFAULT,
|
||||||
|
connection=DEFAULT,
|
||||||
|
_IDMAPPER_CACHE_MAXSIZE=1000,
|
||||||
|
_MAINTENANCE_COUNT=3600 - 1,
|
||||||
|
ServerConfig=DEFAULT) as mocks:
|
||||||
|
mocks['connection'].close = MagicMock()
|
||||||
|
mocks['ServerConfig'].objects.conf = MagicMock(return_value=100)
|
||||||
|
with patch("evennia.server.server.evennia.ScriptDB.objects.validate") as mock:
|
||||||
|
self.server._server_maintenance()
|
||||||
|
mocks['_FLUSH_CACHE'].assert_called_with(1000)
|
||||||
|
mock.assert_called()
|
||||||
|
|
||||||
|
def test__server_maintenance_channel_handler_update(self):
|
||||||
|
with patch.multiple("evennia.server.server",
|
||||||
|
LoopingCall=DEFAULT,
|
||||||
|
Evennia=DEFAULT,
|
||||||
|
_FLUSH_CACHE=DEFAULT,
|
||||||
|
connection=DEFAULT,
|
||||||
|
_IDMAPPER_CACHE_MAXSIZE=1000,
|
||||||
|
_MAINTENANCE_COUNT=3700 - 1,
|
||||||
|
ServerConfig=DEFAULT) as mocks:
|
||||||
|
mocks['connection'].close = MagicMock()
|
||||||
|
mocks['ServerConfig'].objects.conf = MagicMock(return_value=100)
|
||||||
|
with patch("evennia.server.server.evennia.CHANNEL_HANDLER.update") as mock:
|
||||||
|
self.server._server_maintenance()
|
||||||
|
mock.assert_called()
|
||||||
|
|
||||||
|
def test__server_maintenance_close_connection(self):
|
||||||
|
with patch.multiple("evennia.server.server",
|
||||||
|
LoopingCall=DEFAULT,
|
||||||
|
Evennia=DEFAULT,
|
||||||
|
_FLUSH_CACHE=DEFAULT,
|
||||||
|
connection=DEFAULT,
|
||||||
|
_IDMAPPER_CACHE_MAXSIZE=1000,
|
||||||
|
_MAINTENANCE_COUNT=(3600 * 7) - 1,
|
||||||
|
ServerConfig=DEFAULT) as mocks:
|
||||||
|
mocks['connection'].close = MagicMock()
|
||||||
|
mocks['ServerConfig'].objects.conf = MagicMock(return_value=100)
|
||||||
|
self.server._server_maintenance()
|
||||||
|
mocks['connection'].close.assert_called()
|
||||||
|
|
||||||
|
def test__server_maintenance_idle_time(self):
|
||||||
|
with patch.multiple("evennia.server.server",
|
||||||
|
LoopingCall=DEFAULT,
|
||||||
|
Evennia=DEFAULT,
|
||||||
|
_FLUSH_CACHE=DEFAULT,
|
||||||
|
connection=DEFAULT,
|
||||||
|
_IDMAPPER_CACHE_MAXSIZE=1000,
|
||||||
|
_MAINTENANCE_COUNT=(3600 * 7) - 1,
|
||||||
|
SESSIONS=DEFAULT,
|
||||||
|
_IDLE_TIMEOUT=10,
|
||||||
|
time=DEFAULT,
|
||||||
|
ServerConfig=DEFAULT) as mocks:
|
||||||
|
sess1 = MagicMock()
|
||||||
|
sess2 = MagicMock()
|
||||||
|
sess3 = MagicMock()
|
||||||
|
sess4 = MagicMock()
|
||||||
|
sess1.cmd_last = 100 # should time out
|
||||||
|
sess2.cmd_last = 999 # should not time out
|
||||||
|
sess3.cmd_last = 100 # should not time (due to account)
|
||||||
|
sess4.cmd_last = 100 # should time out (due to access)
|
||||||
|
sess1.account = None
|
||||||
|
sess2.account = None
|
||||||
|
sess3.account = MagicMock()
|
||||||
|
sess3.account = MagicMock()
|
||||||
|
sess4.account.access = MagicMock(return_value=False)
|
||||||
|
|
||||||
|
mocks['time'].time = MagicMock(return_value=1000)
|
||||||
|
|
||||||
|
mocks['ServerConfig'].objects.conf = MagicMock(return_value=100)
|
||||||
|
mocks["SESSIONS"].values = MagicMock(return_value=[sess1, sess2, sess3, sess4])
|
||||||
|
mocks["SESSIONS"].disconnect = MagicMock()
|
||||||
|
|
||||||
|
self.server._server_maintenance()
|
||||||
|
reason = "idle timeout exceeded"
|
||||||
|
calls = [call(sess1, reason=reason), call(sess4, reason=reason)]
|
||||||
|
mocks["SESSIONS"].disconnect.assert_has_calls(calls, any_order=True)
|
||||||
|
|
||||||
|
def test_evennia_start(self):
|
||||||
|
with patch.multiple("evennia.server.server",
|
||||||
|
time=DEFAULT,
|
||||||
|
service=DEFAULT) as mocks:
|
||||||
|
|
||||||
|
mocks['time'].time = MagicMock(return_value=1000)
|
||||||
|
evennia = self.server.Evennia(MagicMock())
|
||||||
|
self.assertEqual(evennia.start_time, 1000)
|
||||||
|
|
||||||
|
@patch("evennia.objects.models.ObjectDB")
|
||||||
|
@patch("evennia.server.server.AccountDB")
|
||||||
|
@patch("evennia.server.server.ScriptDB")
|
||||||
|
@patch("evennia.comms.models.ChannelDB")
|
||||||
|
def test_update_defaults(self, mockchan, mockscript, mockacct, mockobj):
|
||||||
|
with patch.multiple("evennia.server.server",
|
||||||
|
ServerConfig=DEFAULT) as mocks:
|
||||||
|
|
||||||
|
mockchan.objects.filter = MagicMock()
|
||||||
|
mockscript.objects.filter = MagicMock()
|
||||||
|
mockacct.objects.filter = MagicMock()
|
||||||
|
mockobj.objects.filter = MagicMock()
|
||||||
|
|
||||||
|
# fake mismatches
|
||||||
|
settings_names = ("CMDSET_CHARACTER", "CMDSET_ACCOUNT",
|
||||||
|
"BASE_ACCOUNT_TYPECLASS", "BASE_OBJECT_TYPECLASS",
|
||||||
|
"BASE_CHARACTER_TYPECLASS", "BASE_ROOM_TYPECLASS",
|
||||||
|
"BASE_EXIT_TYPECLASS", "BASE_SCRIPT_TYPECLASS",
|
||||||
|
"BASE_CHANNEL_TYPECLASS")
|
||||||
|
fakes = {name: "Dummy.path" for name in settings_names}
|
||||||
|
|
||||||
|
def _mock_conf(key, *args):
|
||||||
|
return fakes[key]
|
||||||
|
|
||||||
|
mocks['ServerConfig'].objects.conf = _mock_conf
|
||||||
|
|
||||||
|
evennia = self.server.Evennia(MagicMock())
|
||||||
|
evennia.update_defaults()
|
||||||
|
|
||||||
|
mockchan.objects.filter.assert_called()
|
||||||
|
mockscript.objects.filter.assert_called()
|
||||||
|
mockacct.objects.filter.assert_called()
|
||||||
|
mockobj.objects.filter.assert_called()
|
||||||
|
|
||||||
|
def test_initial_setup(self):
|
||||||
|
from evennia.utils.create import create_account
|
||||||
|
|
||||||
|
acct = create_account("TestSuperuser", "test@test.com", "testpassword",
|
||||||
|
is_superuser=True)
|
||||||
|
|
||||||
|
with patch.multiple("evennia.server.initial_setup",
|
||||||
|
reset_server=DEFAULT,
|
||||||
|
AccountDB=DEFAULT) as mocks:
|
||||||
|
mocks['AccountDB'].objects.get = MagicMock(return_value=acct)
|
||||||
|
evennia = self.server.Evennia(MagicMock())
|
||||||
|
evennia.run_initial_setup()
|
||||||
|
acct.delete()
|
||||||
|
|
||||||
|
def test_initial_setup_retry(self):
|
||||||
|
from evennia.utils.create import create_account
|
||||||
|
|
||||||
|
acct = create_account("TestSuperuser2", "test@test.com", "testpassword",
|
||||||
|
is_superuser=True)
|
||||||
|
|
||||||
|
with patch.multiple("evennia.server.initial_setup",
|
||||||
|
ServerConfig=DEFAULT,
|
||||||
|
reset_server=DEFAULT,
|
||||||
|
AccountDB=DEFAULT) as mocks:
|
||||||
|
mocks['AccountDB'].objects.get = MagicMock(return_value=acct)
|
||||||
|
# a last_initial_setup_step > 0
|
||||||
|
mocks['ServerConfig'].objects.conf = MagicMock(return_value=4)
|
||||||
|
evennia = self.server.Evennia(MagicMock())
|
||||||
|
evennia.run_initial_setup()
|
||||||
|
acct.delete()
|
||||||
|
|
||||||
|
@override_settings(DEFAULT_HOME="#1")
|
||||||
|
def test_run_init_hooks(self):
|
||||||
|
from evennia.utils import create
|
||||||
|
obj1 = create.object(key="HookTestObj1")
|
||||||
|
obj2 = create.object(key="HookTestObj2")
|
||||||
|
acct1 = create.account("HookAcct1", "hooktest1@test.com", "testpasswd")
|
||||||
|
acct2 = create.account("HookAcct2", "hooktest2@test.com", "testpasswd")
|
||||||
|
|
||||||
|
with patch("evennia.objects.models.ObjectDB") as mockobj:
|
||||||
|
with patch("evennia.server.server.AccountDB") as mockacct:
|
||||||
|
|
||||||
|
mockacct.get_all_cached_instances = MagicMock(return_value=[acct1, acct2])
|
||||||
|
mockobj.get_all_cached_instances = MagicMock(return_value=[obj1, obj2])
|
||||||
|
mockobj.objects.clear_all_sessids = MagicMock()
|
||||||
|
|
||||||
|
evennia = self.server.Evennia(MagicMock())
|
||||||
|
evennia.run_init_hooks('reload')
|
||||||
|
evennia.run_init_hooks('reset')
|
||||||
|
evennia.run_init_hooks('shutdown')
|
||||||
|
|
||||||
|
mockacct.get_all_cached_instances.assert_called()
|
||||||
|
mockobj.get_all_cached_instances.assert_called()
|
||||||
|
mockobj.objects.clear_all_sessids.assert_called_with()
|
||||||
|
obj1.delete()
|
||||||
|
obj2.delete()
|
||||||
|
acct1.delete()
|
||||||
|
acct2.delete()
|
||||||
|
|
||||||
|
@patch('evennia.server.server.INFO_DICT', {"test": "foo"})
|
||||||
|
def test_get_info_dict(self):
|
||||||
|
evennia = self.server.Evennia(MagicMock())
|
||||||
|
self.assertEqual(evennia.get_info_dict(), {"test": "foo"})
|
||||||
|
|
@ -383,7 +383,9 @@ class AttributeHandler(object):
|
||||||
|
|
||||||
"""
|
"""
|
||||||
ret = []
|
ret = []
|
||||||
|
category = category.strip().lower() if category is not None else None
|
||||||
for keystr in make_iter(key):
|
for keystr in make_iter(key):
|
||||||
|
keystr = key.strip().lower()
|
||||||
ret.extend(bool(attr) for attr in self._getcache(keystr, category))
|
ret.extend(bool(attr) for attr in self._getcache(keystr, category))
|
||||||
return ret[0] if len(ret) == 1 else ret
|
return ret[0] if len(ret) == 1 else ret
|
||||||
|
|
||||||
|
|
@ -605,7 +607,8 @@ class AttributeHandler(object):
|
||||||
Remove attribute or a list of attributes from object.
|
Remove attribute or a list of attributes from object.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
key (str): An Attribute key to remove.
|
key (str or list): An Attribute key to remove or a list of keys. If
|
||||||
|
multiple keys, they must all be of the same `category`.
|
||||||
raise_exception (bool, optional): If set, not finding the
|
raise_exception (bool, optional): If set, not finding the
|
||||||
Attribute to delete will raise an exception instead of
|
Attribute to delete will raise an exception instead of
|
||||||
just quietly failing.
|
just quietly failing.
|
||||||
|
|
@ -623,7 +626,11 @@ class AttributeHandler(object):
|
||||||
was found matching `key`.
|
was found matching `key`.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
category = category.strip().lower() if category is not None else None
|
||||||
|
|
||||||
for keystr in make_iter(key):
|
for keystr in make_iter(key):
|
||||||
|
keystr = keystr.lower()
|
||||||
|
|
||||||
attr_objs = self._getcache(keystr, category)
|
attr_objs = self._getcache(keystr, category)
|
||||||
for attr_obj in attr_objs:
|
for attr_obj in attr_objs:
|
||||||
if not (
|
if not (
|
||||||
|
|
@ -634,10 +641,11 @@ class AttributeHandler(object):
|
||||||
try:
|
try:
|
||||||
attr_obj.delete()
|
attr_obj.delete()
|
||||||
except AssertionError:
|
except AssertionError:
|
||||||
|
print("Assertionerror for attr.delete()")
|
||||||
# this happens if the attr was already deleted
|
# this happens if the attr was already deleted
|
||||||
pass
|
pass
|
||||||
finally:
|
finally:
|
||||||
self._delcache(key, category)
|
self._delcache(keystr, category)
|
||||||
if not attr_objs and raise_exception:
|
if not attr_objs and raise_exception:
|
||||||
raise AttributeError
|
raise AttributeError
|
||||||
|
|
||||||
|
|
@ -655,6 +663,8 @@ class AttributeHandler(object):
|
||||||
type `attredit` on the Attribute in question.
|
type `attredit` on the Attribute in question.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
category = category.strip().lower() if category is not None else None
|
||||||
|
|
||||||
if not self._cache_complete:
|
if not self._cache_complete:
|
||||||
self._fullcache()
|
self._fullcache()
|
||||||
if accessing_obj:
|
if accessing_obj:
|
||||||
|
|
|
||||||
|
|
@ -402,6 +402,7 @@ def create_account(key, email, password,
|
||||||
typeclass=None,
|
typeclass=None,
|
||||||
is_superuser=False,
|
is_superuser=False,
|
||||||
locks=None, permissions=None,
|
locks=None, permissions=None,
|
||||||
|
tags=None, attributes=None,
|
||||||
report_to=None):
|
report_to=None):
|
||||||
"""
|
"""
|
||||||
This creates a new account.
|
This creates a new account.
|
||||||
|
|
@ -414,15 +415,20 @@ def create_account(key, email, password,
|
||||||
password (str): Password in cleartext.
|
password (str): Password in cleartext.
|
||||||
|
|
||||||
Kwargs:
|
Kwargs:
|
||||||
|
typeclass (str): The typeclass to use for the account.
|
||||||
is_superuser (bool): Wether or not this account is to be a superuser
|
is_superuser (bool): Wether or not this account is to be a superuser
|
||||||
locks (str): Lockstring.
|
locks (str): Lockstring.
|
||||||
permission (list): List of permission strings.
|
permission (list): List of permission strings.
|
||||||
|
tags (list): List of Tags on form `(key, category[, data])`
|
||||||
|
attributes (list): List of Attributes on form
|
||||||
|
`(key, value [, category, [,lockstring [, default_pass]]])`
|
||||||
report_to (Object): An object with a msg() method to report
|
report_to (Object): An object with a msg() method to report
|
||||||
errors to. If not given, errors will be logged.
|
errors to. If not given, errors will be logged.
|
||||||
|
|
||||||
Raises:
|
Raises:
|
||||||
ValueError: If `key` already exists in database.
|
ValueError: If `key` already exists in database.
|
||||||
|
|
||||||
|
|
||||||
Notes:
|
Notes:
|
||||||
Usually only the server admin should need to be superuser, all
|
Usually only the server admin should need to be superuser, all
|
||||||
other access levels can be handled with more fine-grained
|
other access levels can be handled with more fine-grained
|
||||||
|
|
@ -437,6 +443,8 @@ def create_account(key, email, password,
|
||||||
typeclass = typeclass if typeclass else settings.BASE_ACCOUNT_TYPECLASS
|
typeclass = typeclass if typeclass else settings.BASE_ACCOUNT_TYPECLASS
|
||||||
locks = make_iter(locks) if locks is not None else None
|
locks = make_iter(locks) if locks is not None else None
|
||||||
permissions = make_iter(permissions) if permissions is not None else None
|
permissions = make_iter(permissions) if permissions is not None else None
|
||||||
|
tags = make_iter(tags) if tags is not None else None
|
||||||
|
attributes = make_iter(attributes) if attributes is not None else None
|
||||||
|
|
||||||
if isinstance(typeclass, str):
|
if isinstance(typeclass, str):
|
||||||
# a path is given. Load the actual typeclass.
|
# a path is given. Load the actual typeclass.
|
||||||
|
|
@ -462,7 +470,8 @@ def create_account(key, email, password,
|
||||||
is_staff=is_superuser, is_superuser=is_superuser,
|
is_staff=is_superuser, is_superuser=is_superuser,
|
||||||
last_login=now, date_joined=now)
|
last_login=now, date_joined=now)
|
||||||
new_account.set_password(password)
|
new_account.set_password(password)
|
||||||
new_account._createdict = dict(locks=locks, permissions=permissions, report_to=report_to)
|
new_account._createdict = dict(locks=locks, permissions=permissions, report_to=report_to,
|
||||||
|
tags=tags, attributes=attributes)
|
||||||
# saving will trigger the signal that calls the
|
# saving will trigger the signal that calls the
|
||||||
# at_first_save hook on the typeclass, where the _createdict
|
# at_first_save hook on the typeclass, where the _createdict
|
||||||
# can be used.
|
# can be used.
|
||||||
|
|
|
||||||
|
|
@ -510,7 +510,7 @@ class CmdEditorGroup(CmdEditorBase):
|
||||||
else:
|
else:
|
||||||
buf = linebuffer[:lstart] + editor._copy_buffer + linebuffer[lstart:]
|
buf = linebuffer[:lstart] + editor._copy_buffer + linebuffer[lstart:]
|
||||||
editor.update_buffer(buf)
|
editor.update_buffer(buf)
|
||||||
caller.msg("Copied buffer %s to %s." % (editor._copy_buffer, self.lstr))
|
caller.msg("Pasted buffer %s to %s." % (editor._copy_buffer, self.lstr))
|
||||||
elif cmd == ":i":
|
elif cmd == ":i":
|
||||||
# :i <l> <txt> - insert new line
|
# :i <l> <txt> - insert new line
|
||||||
new_lines = self.args.split('\n')
|
new_lines = self.args.split('\n')
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,8 @@
|
||||||
|
"""
|
||||||
|
Various helper resources for writing unittests.
|
||||||
|
|
||||||
|
"""
|
||||||
|
import sys
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.test import TestCase
|
from django.test import TestCase
|
||||||
from mock import Mock
|
from mock import Mock
|
||||||
|
|
@ -10,8 +15,38 @@ from evennia.utils import create
|
||||||
from evennia.utils.idmapper.models import flush_cache
|
from evennia.utils.idmapper.models import flush_cache
|
||||||
|
|
||||||
|
|
||||||
SESSIONS.data_out = Mock()
|
def unload_module(module):
|
||||||
SESSIONS.disconnect = Mock()
|
"""
|
||||||
|
Reset import so one can mock global constants.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
module (module, object or str): The module will
|
||||||
|
be removed so it will have to be imported again. If given
|
||||||
|
an object, the module in which that object sits will be unloaded. A string
|
||||||
|
should directly give the module pathname to unload.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
# (in a test method)
|
||||||
|
unload_module(foo)
|
||||||
|
with mock.patch("foo.GLOBALTHING", "mockval"):
|
||||||
|
import foo
|
||||||
|
... # test code using foo.GLOBALTHING, now set to 'mockval'
|
||||||
|
|
||||||
|
|
||||||
|
This allows for mocking constants global to the module, since
|
||||||
|
otherwise those would not be mocked (since a module is only
|
||||||
|
loaded once).
|
||||||
|
|
||||||
|
"""
|
||||||
|
if isinstance(module, basestring):
|
||||||
|
modulename = module
|
||||||
|
elif hasattr(module, "__module__"):
|
||||||
|
modulename = module.__module__
|
||||||
|
else:
|
||||||
|
modulename = module.__name__
|
||||||
|
|
||||||
|
if modulename in sys.modules:
|
||||||
|
del sys.modules[modulename]
|
||||||
|
|
||||||
|
|
||||||
class EvenniaTest(TestCase):
|
class EvenniaTest(TestCase):
|
||||||
|
|
@ -29,6 +64,11 @@ class EvenniaTest(TestCase):
|
||||||
"""
|
"""
|
||||||
Sets up testing environment
|
Sets up testing environment
|
||||||
"""
|
"""
|
||||||
|
self.backups = (SESSIONS.data_out, SESSIONS.disconnect,
|
||||||
|
settings.DEFAULT_HOME, settings.PROTOTYPE_MODULES)
|
||||||
|
SESSIONS.data_out = Mock()
|
||||||
|
SESSIONS.disconnect = Mock()
|
||||||
|
|
||||||
self.account = create.create_account("TestAccount", email="test@test.com", password="testpassword", typeclass=self.account_typeclass)
|
self.account = create.create_account("TestAccount", email="test@test.com", password="testpassword", typeclass=self.account_typeclass)
|
||||||
self.account2 = create.create_account("TestAccount2", email="test@test.com", password="testpassword", typeclass=self.account_typeclass)
|
self.account2 = create.create_account("TestAccount2", email="test@test.com", password="testpassword", typeclass=self.account_typeclass)
|
||||||
self.room1 = create.create_object(self.room_typeclass, key="Room", nohome=True)
|
self.room1 = create.create_object(self.room_typeclass, key="Room", nohome=True)
|
||||||
|
|
@ -62,6 +102,11 @@ class EvenniaTest(TestCase):
|
||||||
|
|
||||||
def tearDown(self):
|
def tearDown(self):
|
||||||
flush_cache()
|
flush_cache()
|
||||||
|
SESSIONS.data_out = self.backups[0]
|
||||||
|
SESSIONS.disconnect = self.backups[1]
|
||||||
|
settings.DEFAULT_HOME = self.backups[2]
|
||||||
|
settings.PROTOTYPE_MODULES = self.backups[3]
|
||||||
|
|
||||||
del SESSIONS[self.session.sessid]
|
del SESSIONS[self.session.sessid]
|
||||||
self.account.delete()
|
self.account.delete()
|
||||||
self.account2.delete()
|
self.account2.delete()
|
||||||
|
|
|
||||||
93
evennia/utils/tests/test_eveditor.py
Normal file
93
evennia/utils/tests/test_eveditor.py
Normal file
|
|
@ -0,0 +1,93 @@
|
||||||
|
"""
|
||||||
|
Test eveditor
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
from mock import MagicMock
|
||||||
|
from django.test import TestCase
|
||||||
|
from evennia.utils import eveditor
|
||||||
|
from evennia.commands.default.tests import CommandTest
|
||||||
|
|
||||||
|
|
||||||
|
class TestEvEditor(CommandTest):
|
||||||
|
|
||||||
|
def test_eveditor_view_cmd(self):
|
||||||
|
eveditor.EvEditor(self.char1)
|
||||||
|
self.call(eveditor.CmdEditorGroup(), "", cmdstring=":h",
|
||||||
|
msg="<txt> - any non-command is appended to the end of the buffer.")
|
||||||
|
# empty buffer
|
||||||
|
self.call(eveditor.CmdEditorGroup(), "", cmdstring=":",
|
||||||
|
msg="Line Editor []\n01\n[l:01 w:000 c:0000](:h for help)")
|
||||||
|
# input a string
|
||||||
|
self.call(eveditor.CmdLineInput(), "First test line", raw_string="First test line",
|
||||||
|
msg="01First test line")
|
||||||
|
self.call(eveditor.CmdLineInput(), "Second test line", raw_string="Second test line",
|
||||||
|
msg="02Second test line")
|
||||||
|
self.assertEqual(self.char1.ndb._eveditor.get_buffer(), 'First test line\nSecond test line')
|
||||||
|
|
||||||
|
self.call(eveditor.CmdEditorGroup(), "", cmdstring=":", # view buffer
|
||||||
|
msg="Line Editor []\n01First test line\n"
|
||||||
|
"02Second test line\n[l:02 w:006 c:0032](:h for help)")
|
||||||
|
self.call(eveditor.CmdEditorGroup(), "", cmdstring="::", # view buffer, no linenums
|
||||||
|
msg="Line Editor []\nFirst test line\n"
|
||||||
|
"Second test line\n[l:02 w:006 c:0032](:h for help)")
|
||||||
|
self.call(eveditor.CmdEditorGroup(), "", cmdstring=":::", # add single : alone on row
|
||||||
|
msg="Single ':' added to buffer.")
|
||||||
|
self.call(eveditor.CmdEditorGroup(), "", cmdstring=":",
|
||||||
|
msg="Line Editor []\n01First test line\n"
|
||||||
|
"02Second test line\n03:\n[l:03 w:007 c:0034](:h for help)")
|
||||||
|
|
||||||
|
self.call(eveditor.CmdEditorGroup(), "", cmdstring=":dd", # delete line
|
||||||
|
msg="Deleted line 3.")
|
||||||
|
self.assertEqual(self.char1.ndb._eveditor.get_buffer(),
|
||||||
|
'First test line\nSecond test line')
|
||||||
|
self.call(eveditor.CmdEditorGroup(), "", cmdstring=":u", # undo
|
||||||
|
msg="Undid one step.")
|
||||||
|
self.assertEqual(self.char1.ndb._eveditor.get_buffer(),
|
||||||
|
'First test line\nSecond test line\n:')
|
||||||
|
self.call(eveditor.CmdEditorGroup(), "", cmdstring=":uu", # redo
|
||||||
|
msg="Redid one step.")
|
||||||
|
self.assertEqual(self.char1.ndb._eveditor.get_buffer(),
|
||||||
|
'First test line\nSecond test line')
|
||||||
|
self.call(eveditor.CmdEditorGroup(), "", cmdstring=":u", # undo
|
||||||
|
msg="Undid one step.")
|
||||||
|
self.assertEqual(self.char1.ndb._eveditor.get_buffer(),
|
||||||
|
'First test line\nSecond test line\n:')
|
||||||
|
self.call(eveditor.CmdEditorGroup(), "", cmdstring=":",
|
||||||
|
msg="Line Editor []\n01First test line\n"
|
||||||
|
"02Second test line\n03:\n[l:03 w:007 c:0034](:h for help)")
|
||||||
|
|
||||||
|
self.call(eveditor.CmdEditorGroup(), "Second", cmdstring=":dw", # delete by word
|
||||||
|
msg="Removed Second for lines 1-4.")
|
||||||
|
self.call(eveditor.CmdEditorGroup(), "", cmdstring=":u", # undo
|
||||||
|
msg="Undid one step.")
|
||||||
|
self.call(eveditor.CmdEditorGroup(), "2 Second", cmdstring=":dw", # delete by word/line
|
||||||
|
msg="Removed Second for line 2.")
|
||||||
|
self.assertEqual(self.char1.ndb._eveditor.get_buffer(),
|
||||||
|
'First test line\n test line\n:')
|
||||||
|
|
||||||
|
|
||||||
|
self.call(eveditor.CmdEditorGroup(), "2", cmdstring=":p", # paste
|
||||||
|
msg="Copy buffer is empty.")
|
||||||
|
self.call(eveditor.CmdEditorGroup(), "2", cmdstring=":y", # yank
|
||||||
|
msg="Line 2, [' test line'] yanked.")
|
||||||
|
self.call(eveditor.CmdEditorGroup(), "2", cmdstring=":p", # paste
|
||||||
|
msg="Pasted buffer [' test line'] to line 2.")
|
||||||
|
self.assertEqual(self.char1.ndb._eveditor.get_buffer(),
|
||||||
|
'First test line\n test line\n test line\n:')
|
||||||
|
|
||||||
|
self.call(eveditor.CmdEditorGroup(), "3", cmdstring=":x", # cut
|
||||||
|
msg="Line 3, [' test line'] cut.")
|
||||||
|
|
||||||
|
self.call(eveditor.CmdEditorGroup(), "2 New Second line", cmdstring=":i", # insert
|
||||||
|
msg="Inserted 1 new line(s) at line 2.")
|
||||||
|
self.call(eveditor.CmdEditorGroup(), "2 New Replaced Second line", # replace
|
||||||
|
cmdstring=":r", msg="Replaced 1 line(s) at line 2.")
|
||||||
|
self.call(eveditor.CmdEditorGroup(), "2 Inserted-", # insert beginning line
|
||||||
|
cmdstring=":I", msg="Inserted text at beginning of line 2.")
|
||||||
|
self.call(eveditor.CmdEditorGroup(), "2 -End", # append end line
|
||||||
|
cmdstring=":A", msg="Appended text to end of line 2.")
|
||||||
|
|
||||||
|
self.assertEqual(
|
||||||
|
self.char1.ndb._eveditor.get_buffer(),
|
||||||
|
'First test line\nInserted-New Replaced Second line-End\n test line\n:')
|
||||||
|
|
@ -215,6 +215,7 @@ class TextToHTMLparser(object):
|
||||||
text (str): Processed text.
|
text (str): Processed text.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
return text
|
||||||
return text.replace(r'\n', r'<br>')
|
return text.replace(r'\n', r'<br>')
|
||||||
|
|
||||||
def convert_urls(self, text):
|
def convert_urls(self, text):
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
{% extends "base.html" %}
|
{% extends "website/base.html" %}
|
||||||
|
|
||||||
{% block titleblock %}404 - Not Found{% endblock %}
|
{% block titleblock %}404 - Not Found{% endblock %}
|
||||||
|
|
||||||
|
|
@ -10,7 +10,7 @@
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col">
|
<div class="col">
|
||||||
<div class="card">
|
<div class="card">
|
||||||
<div class="card-block">
|
<div class="card-body">
|
||||||
<h1 class="card-title">Error 404</h1>
|
<h1 class="card-title">Error 404</h1>
|
||||||
<h4 class="card-subtitle">Page not found</h2>
|
<h4 class="card-subtitle">Page not found</h2>
|
||||||
<hr />
|
<hr />
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
{% extends "base.html" %}
|
{% extends "website/base.html" %}
|
||||||
|
|
||||||
{% block titleblock %}500 - Internal Server Error{% endblock %}
|
{% block titleblock %}500 - Internal Server Error{% endblock %}
|
||||||
|
|
||||||
|
|
@ -10,7 +10,7 @@
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col">
|
<div class="col">
|
||||||
<div class="card">
|
<div class="card">
|
||||||
<div class="card-block">
|
<div class="card-body">
|
||||||
<h1 class="card-title">Error 500</h1>
|
<h1 class="card-title">Error 500</h1>
|
||||||
<h4 class="card-subtitle">Internal Server Error</h2>
|
<h4 class="card-subtitle">Internal Server Error</h2>
|
||||||
<hr />
|
<hr />
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
{% extends "base.html" %}
|
{% extends "website/base.html" %}
|
||||||
|
|
||||||
{% block titleblock %}Admin{% endblock %}
|
{% block titleblock %}Admin{% endblock %}
|
||||||
|
|
||||||
|
|
@ -6,7 +6,7 @@
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col">
|
<div class="col">
|
||||||
<div class="card">
|
<div class="card">
|
||||||
<div class="card-block p-4">
|
<div class="card-body">
|
||||||
<h1 class="card-title">Admin</h1>
|
<h1 class="card-title">Admin</h1>
|
||||||
<p class="card-text">
|
<p class="card-text">
|
||||||
Welcome to the Evennia Admin Page. Here, you can edit many facets of accounts, characters, and other parts of the game.
|
Welcome to the Evennia Admin Page. Here, you can edit many facets of accounts, characters, and other parts of the game.
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
{% extends "base.html" %}
|
{% extends "website/base.html" %}
|
||||||
|
|
||||||
{% block header_ext %}
|
{% block header_ext %}
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
@ -7,7 +7,7 @@
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col">
|
<div class="col">
|
||||||
<div class="card">
|
<div class="card">
|
||||||
<div class="card-block">
|
<div class="card-body">
|
||||||
<h1 class="card-title">{{flatpage.title}}</h1>
|
<h1 class="card-title">{{flatpage.title}}</h1>
|
||||||
{{flatpage.content}}
|
{{flatpage.content}}
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
{% extends "base.html" %}
|
{% extends "website/base.html" %}
|
||||||
|
|
||||||
{% block titleblock %}Home{% endblock %}
|
{% block titleblock %}Home{% endblock %}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
{% extends "base.html" %}
|
{% extends "website/base.html" %}
|
||||||
|
|
||||||
{% block header_ext %}
|
{% block header_ext %}
|
||||||
<META NAME="ROBOTS" CONTENT="NOINDEX, NOFOLLOW">
|
<META NAME="ROBOTS" CONTENT="NOINDEX, NOFOLLOW">
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
{% extends "base.html" %}
|
{% extends "website/base.html" %}
|
||||||
|
|
||||||
{% block titleblock %}
|
{% block titleblock %}
|
||||||
Login
|
Login
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
{% extends "base.html" %}
|
{% extends "website/base.html" %}
|
||||||
|
|
||||||
{% block titleblock %}To Be Implemented{% endblock %}
|
{% block titleblock %}To Be Implemented{% endblock %}
|
||||||
|
|
||||||
|
|
@ -12,7 +12,7 @@
|
||||||
<div class="col">
|
<div class="col">
|
||||||
<div class="card text-center">
|
<div class="card text-center">
|
||||||
<h1 class="card-header">To Be Implemented...</h1>
|
<h1 class="card-header">To Be Implemented...</h1>
|
||||||
<div class="card-block">
|
<div class="card-body">
|
||||||
This feature has yet to be implemented, but rest assured that it will be!
|
This feature has yet to be implemented, but rest assured that it will be!
|
||||||
<br />
|
<br />
|
||||||
<br />
|
<br />
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue