Merge branch 'master' of https://github.com/evennia/evennia into puzzles
This commit is contained in:
commit
271d5aa0a5
16 changed files with 281 additions and 32 deletions
7
CONTRIBUTING.md
Normal file
7
CONTRIBUTING.md
Normal file
|
|
@ -0,0 +1,7 @@
|
||||||
|
# Contributing to Evennia
|
||||||
|
|
||||||
|
Evennia utilizes GitHub for issue tracking and contributions:
|
||||||
|
|
||||||
|
- Reporting Issues issues/bugs and making feature requests can be done [in the issue tracker](https://github.com/evennia/evennia/issues).
|
||||||
|
- Evennia's documentation is a [wiki](https://github.com/evennia/evennia/wiki) that everyone can contribute to. Further
|
||||||
|
instructions and details about contributing is found [here](https://github.com/evennia/evennia/wiki/Contributing).
|
||||||
|
|
@ -105,7 +105,7 @@ def _create_version():
|
||||||
print(err)
|
print(err)
|
||||||
try:
|
try:
|
||||||
version = "%s (rev %s)" % (version, check_output("git rev-parse --short HEAD", shell=True, cwd=root, stderr=STDOUT).strip())
|
version = "%s (rev %s)" % (version, check_output("git rev-parse --short HEAD", shell=True, cwd=root, stderr=STDOUT).strip())
|
||||||
except (IOError, CalledProcessError):
|
except (IOError, CalledProcessError, WindowsError):
|
||||||
# ignore if we cannot get to git
|
# ignore if we cannot get to git
|
||||||
pass
|
pass
|
||||||
return version
|
return version
|
||||||
|
|
|
||||||
|
|
@ -3091,7 +3091,8 @@ class CmdSpawn(COMMAND_DEFAULT_CLASS):
|
||||||
return
|
return
|
||||||
# we have a prototype, check access
|
# we have a prototype, check access
|
||||||
prototype = prototypes[0]
|
prototype = prototypes[0]
|
||||||
if not caller.locks.check_lockstring(caller, prototype.get('prototype_locks', ''), access_type='spawn'):
|
if not caller.locks.check_lockstring(
|
||||||
|
caller, prototype.get('prototype_locks', ''), access_type='spawn', default=True):
|
||||||
caller.msg("You don't have access to use this prototype.")
|
caller.msg("You don't have access to use this prototype.")
|
||||||
return
|
return
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -744,7 +744,7 @@ hole
|
||||||
the remains of the castle. There is also a standing archway
|
the remains of the castle. There is also a standing archway
|
||||||
offering passage to a path along the old |wsouth|nern inner wall.
|
offering passage to a path along the old |wsouth|nern inner wall.
|
||||||
#
|
#
|
||||||
@detail portoculis;fall;fallen;grating =
|
@detail portcullis;fall;fallen;grating =
|
||||||
This heavy iron grating used to block off the inner part of the gate house, now it has fallen
|
This heavy iron grating used to block off the inner part of the gate house, now it has fallen
|
||||||
to the ground together with the stone archway that once help it up.
|
to the ground together with the stone archway that once help it up.
|
||||||
#
|
#
|
||||||
|
|
@ -786,7 +786,7 @@ archway
|
||||||
The buildings make a half-circle along the main wall, here and there
|
The buildings make a half-circle along the main wall, here and there
|
||||||
broken by falling stone and rubble. At one end (the |wnorth|nern) of
|
broken by falling stone and rubble. At one end (the |wnorth|nern) of
|
||||||
this half-circle is the entrance to the castle, the ruined
|
this half-circle is the entrance to the castle, the ruined
|
||||||
gatehoue. |wEast|nwards from here is some sort of open courtyard.
|
gatehouse. |wEast|nwards from here is some sort of open courtyard.
|
||||||
|
|
||||||
#------------------------------------------------------------
|
#------------------------------------------------------------
|
||||||
#
|
#
|
||||||
|
|
@ -808,7 +808,7 @@ archway
|
||||||
Previously one could probably continue past the obelisk and eastward
|
Previously one could probably continue past the obelisk and eastward
|
||||||
into the castle keep itself, but that way is now completely blocked
|
into the castle keep itself, but that way is now completely blocked
|
||||||
by fallen rubble. To the |wwest|n is the gatehouse and entrance to
|
by fallen rubble. To the |wwest|n is the gatehouse and entrance to
|
||||||
the castle, whereas |wsouth|nwards the collumns make way for a wide
|
the castle, whereas |wsouth|nwards the columns make way for a wide
|
||||||
open courtyard.
|
open courtyard.
|
||||||
#
|
#
|
||||||
@set here/tutorial_info =
|
@set here/tutorial_info =
|
||||||
|
|
|
||||||
15
evennia/game_template/server/logs/README.md
Normal file
15
evennia/game_template/server/logs/README.md
Normal file
|
|
@ -0,0 +1,15 @@
|
||||||
|
This directory contains Evennia's log files. The existence of this README.md file is also necessary
|
||||||
|
to correctly include the log directory in git (since log files are ignored by git and you can't
|
||||||
|
commit an empty directory).
|
||||||
|
|
||||||
|
- `server.log` - log file from the game Server.
|
||||||
|
- `portal.log` - log file from Portal proxy (internet facing)
|
||||||
|
|
||||||
|
Usually these logs are viewed together with `evennia -l`. They are also rotated every week so as not
|
||||||
|
to be too big. Older log names will have a name appended by `_month_date`.
|
||||||
|
|
||||||
|
- `lockwarnings.log` - warnings from the lock system.
|
||||||
|
- `http_requests.log` - this will generally be empty unless turning on debugging inside the server.
|
||||||
|
|
||||||
|
- `channel_<channelname>.log` - these are channel logs for the in-game channels They are also used
|
||||||
|
by the `/history` flag in-game to get the latest message history.
|
||||||
|
|
@ -37,12 +37,15 @@ prototype key (this value must be possible to serialize in an Attribute).
|
||||||
|
|
||||||
from ast import literal_eval
|
from ast import literal_eval
|
||||||
from random import randint as base_randint, random as base_random, choice as base_choice
|
from random import randint as base_randint, random as base_random, choice as base_choice
|
||||||
|
import re
|
||||||
|
|
||||||
from evennia.utils import search
|
from evennia.utils import search
|
||||||
from evennia.utils.utils import justify as base_justify, is_iter, to_str
|
from evennia.utils.utils import justify as base_justify, is_iter, to_str
|
||||||
|
|
||||||
_PROTLIB = None
|
_PROTLIB = None
|
||||||
|
|
||||||
|
_RE_DBREF = re.compile(r"\#[0-9]+")
|
||||||
|
|
||||||
|
|
||||||
# default protfuncs
|
# default protfuncs
|
||||||
|
|
||||||
|
|
@ -325,3 +328,14 @@ def objlist(*args, **kwargs):
|
||||||
|
|
||||||
"""
|
"""
|
||||||
return ["#{}".format(obj.id) for obj in _obj_search(return_list=True, *args, **kwargs)]
|
return ["#{}".format(obj.id) for obj in _obj_search(return_list=True, *args, **kwargs)]
|
||||||
|
|
||||||
|
|
||||||
|
def dbref(*args, **kwargs):
|
||||||
|
"""
|
||||||
|
Usage $dbref(<#dbref>)
|
||||||
|
Returns one Object searched globally by #dbref. Error if #dbref is invalid.
|
||||||
|
"""
|
||||||
|
if not args or len(args) < 1 or _RE_DBREF.match(args[0]) is None:
|
||||||
|
raise ValueError('$dbref requires a valid #dbref argument.')
|
||||||
|
|
||||||
|
return obj(args[0])
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,6 @@ Handling storage of prototypes, both database-based ones (DBPrototypes) and thos
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import re
|
|
||||||
import hashlib
|
import hashlib
|
||||||
import time
|
import time
|
||||||
from ast import literal_eval
|
from ast import literal_eval
|
||||||
|
|
@ -33,8 +32,6 @@ _PROTOTYPE_TAG_CATEGORY = "from_prototype"
|
||||||
_PROTOTYPE_TAG_META_CATEGORY = "db_prototype"
|
_PROTOTYPE_TAG_META_CATEGORY = "db_prototype"
|
||||||
PROT_FUNCS = {}
|
PROT_FUNCS = {}
|
||||||
|
|
||||||
_RE_DBREF = re.compile(r"(?<!\$obj\()(#[0-9]+)")
|
|
||||||
|
|
||||||
|
|
||||||
class PermissionError(RuntimeError):
|
class PermissionError(RuntimeError):
|
||||||
pass
|
pass
|
||||||
|
|
@ -258,7 +255,7 @@ def delete_prototype(prototype_key, caller=None):
|
||||||
stored_prototype = stored_prototype[0]
|
stored_prototype = stored_prototype[0]
|
||||||
if caller:
|
if caller:
|
||||||
if not stored_prototype.access(caller, 'edit'):
|
if not stored_prototype.access(caller, 'edit'):
|
||||||
raise PermissionError("{} does not have permission to "
|
raise PermissionError("{} needs explicit 'edit' permissions to "
|
||||||
"delete prototype {}.".format(caller, prototype_key))
|
"delete prototype {}.".format(caller, prototype_key))
|
||||||
stored_prototype.delete()
|
stored_prototype.delete()
|
||||||
return True
|
return True
|
||||||
|
|
@ -374,14 +371,14 @@ def list_prototypes(caller, key=None, tags=None, show_non_use=False, show_non_ed
|
||||||
display_tuples = []
|
display_tuples = []
|
||||||
for prototype in sorted(prototypes, key=lambda d: d.get('prototype_key', '')):
|
for prototype in sorted(prototypes, key=lambda d: d.get('prototype_key', '')):
|
||||||
lock_use = caller.locks.check_lockstring(
|
lock_use = caller.locks.check_lockstring(
|
||||||
caller, prototype.get('prototype_locks', ''), access_type='spawn')
|
caller, prototype.get('prototype_locks', ''), access_type='spawn', default=True)
|
||||||
if not show_non_use and not lock_use:
|
if not show_non_use and not lock_use:
|
||||||
continue
|
continue
|
||||||
if prototype.get('prototype_key', '') in _MODULE_PROTOTYPES:
|
if prototype.get('prototype_key', '') in _MODULE_PROTOTYPES:
|
||||||
lock_edit = False
|
lock_edit = False
|
||||||
else:
|
else:
|
||||||
lock_edit = caller.locks.check_lockstring(
|
lock_edit = caller.locks.check_lockstring(
|
||||||
caller, prototype.get('prototype_locks', ''), access_type='edit')
|
caller, prototype.get('prototype_locks', ''), access_type='edit', default=True)
|
||||||
if not show_non_edit and not lock_edit:
|
if not show_non_edit and not lock_edit:
|
||||||
continue
|
continue
|
||||||
ptags = []
|
ptags = []
|
||||||
|
|
@ -576,9 +573,6 @@ def protfunc_parser(value, available_functions=None, testing=False, stacktrace=F
|
||||||
|
|
||||||
available_functions = PROT_FUNCS if available_functions is None else available_functions
|
available_functions = PROT_FUNCS if available_functions is None else available_functions
|
||||||
|
|
||||||
# insert $obj(#dbref) for #dbref
|
|
||||||
value = _RE_DBREF.sub("$obj(\\1)", value)
|
|
||||||
|
|
||||||
result = inlinefuncs.parse_inlinefunc(
|
result = inlinefuncs.parse_inlinefunc(
|
||||||
value, available_funcs=available_functions,
|
value, available_funcs=available_functions,
|
||||||
stacktrace=stacktrace, testing=testing, **kwargs)
|
stacktrace=stacktrace, testing=testing, **kwargs)
|
||||||
|
|
@ -713,7 +707,8 @@ def check_permission(prototype_key, action, default=True):
|
||||||
lockstring = prototype.get("prototype_locks")
|
lockstring = prototype.get("prototype_locks")
|
||||||
|
|
||||||
if lockstring:
|
if lockstring:
|
||||||
return check_lockstring(None, lockstring, default=default, access_type=action)
|
return check_lockstring(None, lockstring,
|
||||||
|
default=default, access_type=action)
|
||||||
return default
|
return default
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,7 @@ from evennia.utils.test_resources import EvenniaTest
|
||||||
from evennia.utils.tests.test_evmenu import TestEvMenu
|
from evennia.utils.tests.test_evmenu import TestEvMenu
|
||||||
from evennia.prototypes import spawner, prototypes as protlib
|
from evennia.prototypes import spawner, prototypes as protlib
|
||||||
from evennia.prototypes import menus as olc_menus
|
from evennia.prototypes import menus as olc_menus
|
||||||
|
from evennia.prototypes import protfuncs as protofuncs
|
||||||
|
|
||||||
from evennia.prototypes.prototypes import _PROTOTYPE_TAG_META_CATEGORY
|
from evennia.prototypes.prototypes import _PROTOTYPE_TAG_META_CATEGORY
|
||||||
|
|
||||||
|
|
@ -312,11 +313,83 @@ class TestProtFuncs(EvenniaTest):
|
||||||
self.assertEqual(protlib.protfunc_parser(
|
self.assertEqual(protlib.protfunc_parser(
|
||||||
"$eval({'test': '1', 2:3, 3: $toint(3.5)})"), {'test': '1', 2: 3, 3: 3})
|
"$eval({'test': '1', 2:3, 3: $toint(3.5)})"), {'test': '1', 2: 3, 3: 3})
|
||||||
|
|
||||||
self.assertEqual(protlib.protfunc_parser("$obj(#1)", session=self.session), '#1')
|
|
||||||
self.assertEqual(protlib.protfunc_parser("#1", session=self.session), '#1')
|
# no object search
|
||||||
self.assertEqual(protlib.protfunc_parser("$obj(Char)", session=self.session), '#6')
|
|
||||||
self.assertEqual(protlib.protfunc_parser("$obj(Char)", session=self.session), '#6')
|
with mock.patch("evennia.prototypes.protfuncs._obj_search", wraps=protofuncs._obj_search) as mocked__obj_search:
|
||||||
self.assertEqual(protlib.protfunc_parser("$objlist(#1)", session=self.session), ['#1'])
|
self.assertEqual(protlib.protfunc_parser("obj(#1)", session=self.session), 'obj(#1)')
|
||||||
|
mocked__obj_search.assert_not_called()
|
||||||
|
|
||||||
|
with mock.patch("evennia.prototypes.protfuncs._obj_search", wraps=protofuncs._obj_search) as mocked__obj_search:
|
||||||
|
self.assertEqual(protlib.protfunc_parser("dbref(#1)", session=self.session), 'dbref(#1)')
|
||||||
|
mocked__obj_search.assert_not_called()
|
||||||
|
|
||||||
|
with mock.patch("evennia.prototypes.protfuncs._obj_search", wraps=protofuncs._obj_search) as mocked__obj_search:
|
||||||
|
self.assertEqual(protlib.protfunc_parser("stone(#12345)", session=self.session), 'stone(#12345)')
|
||||||
|
mocked__obj_search.assert_not_called()
|
||||||
|
|
||||||
|
with mock.patch("evennia.prototypes.protfuncs._obj_search", wraps=protofuncs._obj_search) as mocked__obj_search:
|
||||||
|
self.assertEqual(protlib.protfunc_parser("#1", session=self.session), '#1')
|
||||||
|
mocked__obj_search.assert_not_called()
|
||||||
|
|
||||||
|
with mock.patch("evennia.prototypes.protfuncs._obj_search", wraps=protofuncs._obj_search) as mocked__obj_search:
|
||||||
|
self.assertEqual(protlib.protfunc_parser("#12345", session=self.session), '#12345')
|
||||||
|
mocked__obj_search.assert_not_called()
|
||||||
|
|
||||||
|
with mock.patch("evennia.prototypes.protfuncs._obj_search", wraps=protofuncs._obj_search) as mocked__obj_search:
|
||||||
|
self.assertEqual(protlib.protfunc_parser("nothing(#1)", session=self.session), 'nothing(#1)')
|
||||||
|
mocked__obj_search.assert_not_called()
|
||||||
|
|
||||||
|
with mock.patch("evennia.prototypes.protfuncs._obj_search", wraps=protofuncs._obj_search) as mocked__obj_search:
|
||||||
|
self.assertEqual(protlib.protfunc_parser("(#12345)", session=self.session), '(#12345)')
|
||||||
|
mocked__obj_search.assert_not_called()
|
||||||
|
|
||||||
|
with mock.patch("evennia.prototypes.protfuncs._obj_search", wraps=protofuncs._obj_search) as mocked__obj_search:
|
||||||
|
self.assertEqual(protlib.protfunc_parser("obj(Char)", session=self.session), 'obj(Char)')
|
||||||
|
mocked__obj_search.assert_not_called()
|
||||||
|
|
||||||
|
with mock.patch("evennia.prototypes.protfuncs._obj_search", wraps=protofuncs._obj_search) as mocked__obj_search:
|
||||||
|
self.assertEqual(protlib.protfunc_parser("objlist(#1)", session=self.session), 'objlist(#1)')
|
||||||
|
mocked__obj_search.assert_not_called()
|
||||||
|
|
||||||
|
with mock.patch("evennia.prototypes.protfuncs._obj_search", wraps=protofuncs._obj_search) as mocked__obj_search:
|
||||||
|
self.assertEqual(protlib.protfunc_parser("dbref(Char)", session=self.session), 'dbref(Char)')
|
||||||
|
mocked__obj_search.assert_not_called()
|
||||||
|
|
||||||
|
|
||||||
|
# obj search happens
|
||||||
|
|
||||||
|
with mock.patch("evennia.prototypes.protfuncs._obj_search", wraps=protofuncs._obj_search) as mocked__obj_search:
|
||||||
|
self.assertEqual(protlib.protfunc_parser("$objlist(#1)", session=self.session), ['#1'])
|
||||||
|
mocked__obj_search.assert_called_once()
|
||||||
|
assert ('#1',) == mocked__obj_search.call_args[0]
|
||||||
|
|
||||||
|
with mock.patch("evennia.prototypes.protfuncs._obj_search", wraps=protofuncs._obj_search) as mocked__obj_search:
|
||||||
|
self.assertEqual(protlib.protfunc_parser("$obj(#1)", session=self.session), '#1')
|
||||||
|
mocked__obj_search.assert_called_once()
|
||||||
|
assert ('#1',) == mocked__obj_search.call_args[0]
|
||||||
|
|
||||||
|
with mock.patch("evennia.prototypes.protfuncs._obj_search", wraps=protofuncs._obj_search) as mocked__obj_search:
|
||||||
|
self.assertEqual(protlib.protfunc_parser("$dbref(#1)", session=self.session), '#1')
|
||||||
|
mocked__obj_search.assert_called_once()
|
||||||
|
assert ('#1',) == mocked__obj_search.call_args[0]
|
||||||
|
|
||||||
|
with mock.patch("evennia.prototypes.protfuncs._obj_search", wraps=protofuncs._obj_search) as mocked__obj_search:
|
||||||
|
self.assertEqual(protlib.protfunc_parser("$obj(Char)", session=self.session), '#6')
|
||||||
|
mocked__obj_search.assert_called_once()
|
||||||
|
assert ('Char',) == mocked__obj_search.call_args[0]
|
||||||
|
|
||||||
|
|
||||||
|
# bad invocation
|
||||||
|
|
||||||
|
with mock.patch("evennia.prototypes.protfuncs._obj_search", wraps=protofuncs._obj_search) as mocked__obj_search:
|
||||||
|
self.assertEqual(protlib.protfunc_parser("$badfunc(#1)", session=self.session), '<UNKNOWN>')
|
||||||
|
mocked__obj_search.assert_not_called()
|
||||||
|
|
||||||
|
with mock.patch("evennia.prototypes.protfuncs._obj_search", wraps=protofuncs._obj_search) as mocked__obj_search:
|
||||||
|
self.assertRaises(ValueError, protlib.protfunc_parser, "$dbref(Char)")
|
||||||
|
mocked__obj_search.assert_not_called()
|
||||||
|
|
||||||
|
|
||||||
self.assertEqual(protlib.value_to_obj(
|
self.assertEqual(protlib.value_to_obj(
|
||||||
protlib.protfunc_parser("#6", session=self.session)), self.char1)
|
protlib.protfunc_parser("#6", session=self.session)), self.char1)
|
||||||
|
|
|
||||||
|
|
@ -98,7 +98,10 @@ TWISTED_MIN = '18.0.0'
|
||||||
DJANGO_MIN = '1.11'
|
DJANGO_MIN = '1.11'
|
||||||
DJANGO_REC = '1.11'
|
DJANGO_REC = '1.11'
|
||||||
|
|
||||||
sys.path[1] = EVENNIA_ROOT
|
try:
|
||||||
|
sys.path[1] = EVENNIA_ROOT
|
||||||
|
except IndexError:
|
||||||
|
sys.path.append(EVENNIA_ROOT)
|
||||||
|
|
||||||
# ------------------------------------------------------------
|
# ------------------------------------------------------------
|
||||||
#
|
#
|
||||||
|
|
@ -222,6 +225,19 @@ RECREATED_SETTINGS = \
|
||||||
their accounts with their old passwords.
|
their accounts with their old passwords.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
ERROR_INITMISSING = \
|
||||||
|
"""
|
||||||
|
ERROR: 'evennia --initmissing' must be called from the root of
|
||||||
|
your game directory, since it tries to create any missing files
|
||||||
|
in the server/ subfolder.
|
||||||
|
"""
|
||||||
|
|
||||||
|
RECREATED_MISSING = \
|
||||||
|
"""
|
||||||
|
(Re)created any missing directories or files. Evennia should
|
||||||
|
be ready to run now!
|
||||||
|
"""
|
||||||
|
|
||||||
ERROR_DATABASE = \
|
ERROR_DATABASE = \
|
||||||
"""
|
"""
|
||||||
ERROR: Your database does not seem to be set up correctly.
|
ERROR: Your database does not seem to be set up correctly.
|
||||||
|
|
@ -261,7 +277,7 @@ INFO_WINDOWS_BATFILE = \
|
||||||
twistd.bat to point to the actual location of the Twisted
|
twistd.bat to point to the actual location of the Twisted
|
||||||
executable (usually called twistd.py) on your machine.
|
executable (usually called twistd.py) on your machine.
|
||||||
|
|
||||||
This procedure is only done once. Run evennia.py again when you
|
This procedure is only done once. Run `evennia` again when you
|
||||||
are ready to start the server.
|
are ready to start the server.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
@ -1201,7 +1217,7 @@ def evennia_version():
|
||||||
"git rev-parse --short HEAD",
|
"git rev-parse --short HEAD",
|
||||||
shell=True, cwd=EVENNIA_ROOT, stderr=STDOUT).strip()
|
shell=True, cwd=EVENNIA_ROOT, stderr=STDOUT).strip()
|
||||||
version = "%s (rev %s)" % (version, rev)
|
version = "%s (rev %s)" % (version, rev)
|
||||||
except (IOError, CalledProcessError):
|
except (IOError, CalledProcessError, WindowsError):
|
||||||
# move on if git is not answering
|
# move on if git is not answering
|
||||||
pass
|
pass
|
||||||
return version
|
return version
|
||||||
|
|
@ -1331,7 +1347,10 @@ def create_settings_file(init=True, secret_settings=False):
|
||||||
else:
|
else:
|
||||||
print("Reset the settings file.")
|
print("Reset the settings file.")
|
||||||
|
|
||||||
default_settings_path = os.path.join(EVENNIA_TEMPLATE, "server", "conf", "settings.py")
|
if secret_settings:
|
||||||
|
default_settings_path = os.path.join(EVENNIA_TEMPLATE, "server", "conf", "secret_settings.py")
|
||||||
|
else:
|
||||||
|
default_settings_path = os.path.join(EVENNIA_TEMPLATE, "server", "conf", "settings.py")
|
||||||
shutil.copy(default_settings_path, settings_path)
|
shutil.copy(default_settings_path, settings_path)
|
||||||
|
|
||||||
with open(settings_path, 'r') as f:
|
with open(settings_path, 'r') as f:
|
||||||
|
|
@ -1625,7 +1644,7 @@ def error_check_python_modules():
|
||||||
#
|
#
|
||||||
# ------------------------------------------------------------
|
# ------------------------------------------------------------
|
||||||
|
|
||||||
def init_game_directory(path, check_db=True):
|
def init_game_directory(path, check_db=True, need_gamedir=True):
|
||||||
"""
|
"""
|
||||||
Try to analyze the given path to find settings.py - this defines
|
Try to analyze the given path to find settings.py - this defines
|
||||||
the game directory and also sets PYTHONPATH as well as the django
|
the game directory and also sets PYTHONPATH as well as the django
|
||||||
|
|
@ -1634,15 +1653,17 @@ def init_game_directory(path, check_db=True):
|
||||||
Args:
|
Args:
|
||||||
path (str): Path to new game directory, including its name.
|
path (str): Path to new game directory, including its name.
|
||||||
check_db (bool, optional): Check if the databae exists.
|
check_db (bool, optional): Check if the databae exists.
|
||||||
|
need_gamedir (bool, optional): set to False if Evennia doesn't require to be run in a valid game directory.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
# set the GAMEDIR path
|
# set the GAMEDIR path
|
||||||
set_gamedir(path)
|
if need_gamedir:
|
||||||
|
set_gamedir(path)
|
||||||
|
|
||||||
# Add gamedir to python path
|
# Add gamedir to python path
|
||||||
sys.path.insert(0, GAMEDIR)
|
sys.path.insert(0, GAMEDIR)
|
||||||
|
|
||||||
if TEST_MODE:
|
if TEST_MODE or not need_gamedir:
|
||||||
if ENFORCED_SETTING:
|
if ENFORCED_SETTING:
|
||||||
print(NOTE_TEST_CUSTOM.format(settings_dotpath=SETTINGS_DOTPATH))
|
print(NOTE_TEST_CUSTOM.format(settings_dotpath=SETTINGS_DOTPATH))
|
||||||
os.environ['DJANGO_SETTINGS_MODULE'] = SETTINGS_DOTPATH
|
os.environ['DJANGO_SETTINGS_MODULE'] = SETTINGS_DOTPATH
|
||||||
|
|
@ -1669,6 +1690,10 @@ def init_game_directory(path, check_db=True):
|
||||||
if check_db:
|
if check_db:
|
||||||
check_database()
|
check_database()
|
||||||
|
|
||||||
|
# if we don't have to check the game directory, return right away
|
||||||
|
if not need_gamedir:
|
||||||
|
return
|
||||||
|
|
||||||
# set up the Evennia executables and log file locations
|
# set up the Evennia executables and log file locations
|
||||||
global AMP_PORT, AMP_HOST, AMP_INTERFACE
|
global AMP_PORT, AMP_HOST, AMP_INTERFACE
|
||||||
global SERVER_PY_FILE, PORTAL_PY_FILE
|
global SERVER_PY_FILE, PORTAL_PY_FILE
|
||||||
|
|
@ -1914,6 +1939,10 @@ def main():
|
||||||
'--initsettings', action='store_true', dest="initsettings",
|
'--initsettings', action='store_true', dest="initsettings",
|
||||||
default=False,
|
default=False,
|
||||||
help="create a new, empty settings file as\n gamedir/server/conf/settings.py")
|
help="create a new, empty settings file as\n gamedir/server/conf/settings.py")
|
||||||
|
parser.add_argument(
|
||||||
|
'--initmissing', action='store_true', dest="initmissing",
|
||||||
|
default=False,
|
||||||
|
help="checks for missing secret_settings or server logs\n directory, and adds them if needed")
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
'--profiler', action='store_true', dest='profiler', default=False,
|
'--profiler', action='store_true', dest='profiler', default=False,
|
||||||
help="start given server component under the Python profiler")
|
help="start given server component under the Python profiler")
|
||||||
|
|
@ -1987,6 +2016,21 @@ def main():
|
||||||
print(ERROR_INITSETTINGS)
|
print(ERROR_INITSETTINGS)
|
||||||
sys.exit()
|
sys.exit()
|
||||||
|
|
||||||
|
if args.initmissing:
|
||||||
|
try:
|
||||||
|
log_path = os.path.join(SERVERDIR, "logs")
|
||||||
|
if not os.path.exists(log_path):
|
||||||
|
os.makedirs(log_path)
|
||||||
|
|
||||||
|
settings_path = os.path.join(CONFDIR, "secret_settings.py")
|
||||||
|
if not os.path.exists(settings_path):
|
||||||
|
create_settings_file(init=False, secret_settings=True)
|
||||||
|
|
||||||
|
print(RECREATED_MISSING)
|
||||||
|
except IOError:
|
||||||
|
print(ERROR_INITMISSING)
|
||||||
|
sys.exit()
|
||||||
|
|
||||||
if args.tail_log:
|
if args.tail_log:
|
||||||
# set up for tailing the log files
|
# set up for tailing the log files
|
||||||
global NO_REACTOR_STOP
|
global NO_REACTOR_STOP
|
||||||
|
|
@ -2053,6 +2097,10 @@ def main():
|
||||||
elif option != "noop":
|
elif option != "noop":
|
||||||
# pass-through to django manager
|
# pass-through to django manager
|
||||||
check_db = False
|
check_db = False
|
||||||
|
need_gamedir = True
|
||||||
|
# some commands don't require the presence of a game directory to work
|
||||||
|
if option in ('makemessages', 'compilemessages'):
|
||||||
|
need_gamedir = False
|
||||||
|
|
||||||
# handle special django commands
|
# handle special django commands
|
||||||
if option in ('runserver', 'testserver'):
|
if option in ('runserver', 'testserver'):
|
||||||
|
|
@ -2065,7 +2113,7 @@ def main():
|
||||||
global TEST_MODE
|
global TEST_MODE
|
||||||
TEST_MODE = True
|
TEST_MODE = True
|
||||||
|
|
||||||
init_game_directory(CURRENT_DIR, check_db=check_db)
|
init_game_directory(CURRENT_DIR, check_db=check_db, need_gamedir=need_gamedir)
|
||||||
|
|
||||||
# pass on to the manager
|
# pass on to the manager
|
||||||
args = [option]
|
args = [option]
|
||||||
|
|
@ -2081,6 +2129,11 @@ def main():
|
||||||
kwargs[arg.lstrip("--")] = value
|
kwargs[arg.lstrip("--")] = value
|
||||||
else:
|
else:
|
||||||
args.append(arg)
|
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:
|
try:
|
||||||
django.core.management.call_command(*args, **kwargs)
|
django.core.management.call_command(*args, **kwargs)
|
||||||
except django.core.management.base.CommandError as exc:
|
except django.core.management.base.CommandError as exc:
|
||||||
|
|
|
||||||
|
|
@ -421,7 +421,7 @@ class IRCBotFactory(protocol.ReconnectingClientFactory):
|
||||||
|
|
||||||
def clientConnectionLost(self, connector, reason):
|
def clientConnectionLost(self, connector, reason):
|
||||||
"""
|
"""
|
||||||
Called when Client looses connection.
|
Called when Client loses connection.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
connector (Connection): Represents the connection.
|
connector (Connection): Represents the connection.
|
||||||
|
|
|
||||||
|
|
@ -115,7 +115,7 @@ AMP_INTERFACE = '127.0.0.1'
|
||||||
EVENNIA_DIR = os.path.dirname(os.path.abspath(__file__))
|
EVENNIA_DIR = os.path.dirname(os.path.abspath(__file__))
|
||||||
# Path to the game directory (containing the server/conf/settings.py file)
|
# Path to the game directory (containing the server/conf/settings.py file)
|
||||||
# This is dynamically created- there is generally no need to change this!
|
# This is dynamically created- there is generally no need to change this!
|
||||||
if sys.argv[1] == 'test' if len(sys.argv) > 1 else False:
|
if EVENNIA_DIR.lower() == os.getcwd().lower() or (sys.argv[1] == 'test' if len(sys.argv) > 1 else False):
|
||||||
# unittesting mode
|
# unittesting mode
|
||||||
GAME_DIR = os.getcwd()
|
GAME_DIR = os.getcwd()
|
||||||
else:
|
else:
|
||||||
|
|
@ -138,7 +138,7 @@ HTTP_LOG_FILE = os.path.join(LOG_DIR, 'http_requests.log')
|
||||||
LOCKWARNING_LOG_FILE = os.path.join(LOG_DIR, 'lockwarnings.log')
|
LOCKWARNING_LOG_FILE = os.path.join(LOG_DIR, 'lockwarnings.log')
|
||||||
# Rotate log files when server and/or portal stops. This will keep log
|
# Rotate log files when server and/or portal stops. This will keep log
|
||||||
# file sizes down. Turn off to get ever growing log files and never
|
# file sizes down. Turn off to get ever growing log files and never
|
||||||
# loose log info.
|
# lose log info.
|
||||||
CYCLE_LOGFILES = True
|
CYCLE_LOGFILES = True
|
||||||
# Number of lines to append to rotating channel logs when they rotate
|
# Number of lines to append to rotating channel logs when they rotate
|
||||||
CHANNEL_LOG_NUM_TAIL_LINES = 20
|
CHANNEL_LOG_NUM_TAIL_LINES = 20
|
||||||
|
|
|
||||||
|
|
@ -229,6 +229,12 @@ def create_script(typeclass=None, key=None, obj=None, account=None, locks=None,
|
||||||
# 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.
|
||||||
new_script.save()
|
new_script.save()
|
||||||
|
|
||||||
|
if not new_script.id:
|
||||||
|
# this happens in the case of having a repeating script with `repeats=1` and
|
||||||
|
# `start_delay=False` - the script will run once and immediately stop before save is over.
|
||||||
|
return None
|
||||||
|
|
||||||
return new_script
|
return new_script
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -397,6 +397,11 @@ class SharedMemoryModel(with_metaclass(SharedMemoryModelBase, Model)):
|
||||||
super(SharedMemoryModel, cls).save(*args, **kwargs)
|
super(SharedMemoryModel, cls).save(*args, **kwargs)
|
||||||
callFromThread(_save_callback, self, *args, **kwargs)
|
callFromThread(_save_callback, self, *args, **kwargs)
|
||||||
|
|
||||||
|
if not self.pk:
|
||||||
|
# this can happen if some of the startup methods immediately
|
||||||
|
# delete the object (an example are Scripts that start and die immediately)
|
||||||
|
return
|
||||||
|
|
||||||
# update field-update hooks and eventual OOB watchers
|
# update field-update hooks and eventual OOB watchers
|
||||||
new = False
|
new = False
|
||||||
if "update_fields" in kwargs and kwargs["update_fields"]:
|
if "update_fields" in kwargs and kwargs["update_fields"]:
|
||||||
|
|
@ -421,6 +426,7 @@ class SharedMemoryModel(with_metaclass(SharedMemoryModelBase, Model)):
|
||||||
# fieldtracker = "_oob_at_%s_postsave" % fieldname
|
# fieldtracker = "_oob_at_%s_postsave" % fieldname
|
||||||
# if hasattr(self, fieldtracker):
|
# if hasattr(self, fieldtracker):
|
||||||
# _GA(self, fieldtracker)(fieldname)
|
# _GA(self, fieldtracker)(fieldname)
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
class WeakSharedMemoryModelBase(SharedMemoryModelBase):
|
class WeakSharedMemoryModelBase(SharedMemoryModelBase):
|
||||||
|
|
|
||||||
79
evennia/utils/tests/test_create_functions.py
Normal file
79
evennia/utils/tests/test_create_functions.py
Normal file
|
|
@ -0,0 +1,79 @@
|
||||||
|
"""
|
||||||
|
Tests of create functions
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
from evennia.utils.test_resources import EvenniaTest
|
||||||
|
from evennia.scripts.scripts import DefaultScript
|
||||||
|
from evennia.utils import create
|
||||||
|
|
||||||
|
|
||||||
|
class TestCreateScript(EvenniaTest):
|
||||||
|
|
||||||
|
def test_create_script(self):
|
||||||
|
class TestScriptA(DefaultScript):
|
||||||
|
def at_script_creation(self):
|
||||||
|
self.key = 'test_script'
|
||||||
|
self.interval = 10
|
||||||
|
self.persistent = False
|
||||||
|
|
||||||
|
script = create.create_script(TestScriptA, key='test_script')
|
||||||
|
assert script is not None
|
||||||
|
assert script.interval == 10
|
||||||
|
assert script.key == 'test_script'
|
||||||
|
script.stop()
|
||||||
|
|
||||||
|
def test_create_script_w_repeats_equal_1(self):
|
||||||
|
class TestScriptB(DefaultScript):
|
||||||
|
def at_script_creation(self):
|
||||||
|
self.key = 'test_script'
|
||||||
|
self.interval = 10
|
||||||
|
self.repeats = 1
|
||||||
|
self.persistent = False
|
||||||
|
|
||||||
|
# script is already stopped (interval=1, start_delay=False)
|
||||||
|
script = create.create_script(TestScriptB, key='test_script')
|
||||||
|
assert script is None
|
||||||
|
|
||||||
|
def test_create_script_w_repeats_equal_1_persisted(self):
|
||||||
|
class TestScriptB1(DefaultScript):
|
||||||
|
def at_script_creation(self):
|
||||||
|
self.key = 'test_script'
|
||||||
|
self.interval = 10
|
||||||
|
self.repeats = 1
|
||||||
|
self.persistent = True
|
||||||
|
|
||||||
|
# script is already stopped (interval=1, start_delay=False)
|
||||||
|
script = create.create_script(TestScriptB1, key='test_script')
|
||||||
|
assert script is None
|
||||||
|
|
||||||
|
def test_create_script_w_repeats_equal_2(self):
|
||||||
|
class TestScriptC(DefaultScript):
|
||||||
|
def at_script_creation(self):
|
||||||
|
self.key = 'test_script'
|
||||||
|
self.interval = 10
|
||||||
|
self.repeats = 2
|
||||||
|
self.persistent = False
|
||||||
|
|
||||||
|
script = create.create_script(TestScriptC, key='test_script')
|
||||||
|
assert script is not None
|
||||||
|
assert script.interval == 10
|
||||||
|
assert script.repeats == 2
|
||||||
|
assert script.key == 'test_script'
|
||||||
|
script.stop()
|
||||||
|
|
||||||
|
def test_create_script_w_repeats_equal_1_and_delayed(self):
|
||||||
|
class TestScriptD(DefaultScript):
|
||||||
|
def at_script_creation(self):
|
||||||
|
self.key = 'test_script'
|
||||||
|
self.interval = 10
|
||||||
|
self.start_delay = True
|
||||||
|
self.repeats = 1
|
||||||
|
self.persistent = False
|
||||||
|
|
||||||
|
script = create.create_script(TestScriptD, key='test_script')
|
||||||
|
assert script is not None
|
||||||
|
assert script.interval == 10
|
||||||
|
assert script.repeats == 1
|
||||||
|
assert script.key == 'test_script'
|
||||||
|
script.stop()
|
||||||
|
|
@ -64,7 +64,7 @@ JQuery available.
|
||||||
<script src={% static "webclient/js/evennia.js" %} language="javascript" type="text/javascript" charset="utf-8"/></script>
|
<script src={% static "webclient/js/evennia.js" %} language="javascript" type="text/javascript" charset="utf-8"/></script>
|
||||||
|
|
||||||
<!-- set up splits before loading the GUI -->
|
<!-- set up splits before loading the GUI -->
|
||||||
<script src="https://unpkg.com/split.js/split.min.js"></script>
|
<script src="https://unpkg.com/split.js@1.5.9/dist/split.min.js"></script>
|
||||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/mustache.js/2.3.0/mustache.min.js"></script>
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/mustache.js/2.3.0/mustache.min.js"></script>
|
||||||
|
|
||||||
<!-- Load gui library -->
|
<!-- Load gui library -->
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue