Merge branch 'main' of https://github.com/evennia/evennia into editor_search_traceback
This commit is contained in:
commit
c21c5667c9
25 changed files with 503 additions and 188 deletions
|
|
@ -1 +1 @@
|
|||
4.1.0
|
||||
4.1.1
|
||||
|
|
|
|||
|
|
@ -16,13 +16,14 @@ import time
|
|||
import typing
|
||||
from random import getrandbits
|
||||
|
||||
import evennia
|
||||
from django.conf import settings
|
||||
from django.contrib.auth import authenticate, password_validation
|
||||
from django.core.exceptions import ImproperlyConfigured, ValidationError
|
||||
from django.utils import timezone
|
||||
from django.utils.module_loading import import_string
|
||||
from django.utils.translation import gettext as _
|
||||
|
||||
import evennia
|
||||
from evennia.accounts.manager import AccountManager
|
||||
from evennia.accounts.models import AccountDB
|
||||
from evennia.commands.cmdsethandler import CmdSetHandler
|
||||
|
|
@ -30,17 +31,24 @@ from evennia.comms.models import ChannelDB
|
|||
from evennia.objects.models import ObjectDB
|
||||
from evennia.scripts.scripthandler import ScriptHandler
|
||||
from evennia.server.models import ServerConfig
|
||||
from evennia.server.signals import (SIGNAL_ACCOUNT_POST_CREATE,
|
||||
SIGNAL_ACCOUNT_POST_LOGIN_FAIL,
|
||||
SIGNAL_OBJECT_POST_PUPPET,
|
||||
SIGNAL_OBJECT_POST_UNPUPPET)
|
||||
from evennia.server.signals import (
|
||||
SIGNAL_ACCOUNT_POST_CREATE,
|
||||
SIGNAL_ACCOUNT_POST_LOGIN_FAIL,
|
||||
SIGNAL_OBJECT_POST_PUPPET,
|
||||
SIGNAL_OBJECT_POST_UNPUPPET,
|
||||
)
|
||||
from evennia.server.throttle import Throttle
|
||||
from evennia.typeclasses.attributes import ModelAttributeBackend, NickHandler
|
||||
from evennia.typeclasses.models import TypeclassBase
|
||||
from evennia.utils import class_from_module, create, logger
|
||||
from evennia.utils.optionhandler import OptionHandler
|
||||
from evennia.utils.utils import (is_iter, lazy_property, make_iter, to_str,
|
||||
variable_from_module)
|
||||
from evennia.utils.utils import (
|
||||
is_iter,
|
||||
lazy_property,
|
||||
make_iter,
|
||||
to_str,
|
||||
variable_from_module,
|
||||
)
|
||||
|
||||
__all__ = ("DefaultAccount", "DefaultGuest")
|
||||
|
||||
|
|
|
|||
|
|
@ -5,13 +5,13 @@ Building and world design commands
|
|||
import re
|
||||
import typing
|
||||
|
||||
import evennia
|
||||
from django.conf import settings
|
||||
from django.core.paginator import Paginator
|
||||
from django.db.models import Max, Min, Q
|
||||
|
||||
import evennia
|
||||
from evennia import InterruptCommand
|
||||
from evennia.commands.cmdhandler import (generate_cmdset_providers,
|
||||
get_and_merge_cmdsets)
|
||||
from evennia.commands.cmdhandler import generate_cmdset_providers, get_and_merge_cmdsets
|
||||
from evennia.locks.lockhandler import LockException
|
||||
from evennia.objects.models import ObjectDB
|
||||
from evennia.prototypes import menus as olc_menus
|
||||
|
|
@ -24,10 +24,18 @@ from evennia.utils.dbserialize import deserialize
|
|||
from evennia.utils.eveditor import EvEditor
|
||||
from evennia.utils.evmore import EvMore
|
||||
from evennia.utils.evtable import EvTable
|
||||
from evennia.utils.utils import (class_from_module, crop, dbref, display_len,
|
||||
format_grid, get_all_typeclasses,
|
||||
inherits_from, interactive, list_to_string,
|
||||
variable_from_module)
|
||||
from evennia.utils.utils import (
|
||||
class_from_module,
|
||||
crop,
|
||||
dbref,
|
||||
display_len,
|
||||
format_grid,
|
||||
get_all_typeclasses,
|
||||
inherits_from,
|
||||
interactive,
|
||||
list_to_string,
|
||||
variable_from_module,
|
||||
)
|
||||
|
||||
COMMAND_DEFAULT_CLASS = class_from_module(settings.COMMAND_DEFAULT_CLASS)
|
||||
|
||||
|
|
@ -1397,7 +1405,7 @@ class CmdSetHome(CmdLink):
|
|||
obj.home = new_home
|
||||
if old_home:
|
||||
string = (
|
||||
f"Home location of {obj} was changed from {old_home}({old_home.dbref} to"
|
||||
f"Home location of {obj} was changed from {old_home}({old_home.dbref}) to"
|
||||
f" {new_home}({new_home.dbref})."
|
||||
)
|
||||
else:
|
||||
|
|
@ -3274,11 +3282,15 @@ class CmdFind(COMMAND_DEFAULT_CLASS):
|
|||
string += f"\n |RNo match found for '{searchstring}' in #dbref interval.|n"
|
||||
else:
|
||||
result = result[0]
|
||||
string += (f"\n|g {result.get_display_name(caller)}"
|
||||
f"{result.get_extra_display_name_info(caller)} - {result.path}|n")
|
||||
string += (
|
||||
f"\n|g {result.get_display_name(caller)}"
|
||||
f"{result.get_extra_display_name_info(caller)} - {result.path}|n"
|
||||
)
|
||||
if "loc" in self.switches and not is_account and result.location:
|
||||
string += (f" (|wlocation|n: |g{result.location.get_display_name(caller)}"
|
||||
f"{result.get_extra_display_name_info(caller)}|n)")
|
||||
string += (
|
||||
f" (|wlocation|n: |g{result.location.get_display_name(caller)}"
|
||||
f"{result.get_extra_display_name_info(caller)}|n)"
|
||||
)
|
||||
else:
|
||||
# Not an account/dbref search but a wider search; build a queryset.
|
||||
# Searches for key and aliases
|
||||
|
|
|
|||
|
|
@ -4,8 +4,9 @@ General Character commands usually available to all characters
|
|||
|
||||
import re
|
||||
|
||||
import evennia
|
||||
from django.conf import settings
|
||||
|
||||
import evennia
|
||||
from evennia.typeclasses.attributes import NickTemplateInvalid
|
||||
from evennia.utils import utils
|
||||
|
||||
|
|
@ -397,7 +398,7 @@ class NumberedTargetCommand(COMMAND_DEFAULT_CLASS):
|
|||
"""
|
||||
super().parse()
|
||||
self.number = 0
|
||||
if hasattr(self, "lhs"):
|
||||
if getattr(self, "lhs", None):
|
||||
# handle self.lhs but don't require it
|
||||
count, *args = self.lhs.split(maxsplit=1)
|
||||
# we only use the first word as a count if it's a number and
|
||||
|
|
|
|||
|
|
@ -134,6 +134,17 @@ class TestGeneral(BaseEvenniaCommandTest):
|
|||
self.obj2.location = self.char1
|
||||
self.call(general.CmdGive(), "2 Obj = Char2", "You give two Objs")
|
||||
|
||||
def test_numbered_target_command(self):
|
||||
class CmdTest(general.NumberedTargetCommand):
|
||||
key = "test"
|
||||
|
||||
def func(self):
|
||||
self.msg(f"Number: {self.number} Args: {self.args}")
|
||||
|
||||
self.call(CmdTest(), "", "Number: 0 Args: ")
|
||||
self.call(CmdTest(), "obj", "Number: 0 Args: obj")
|
||||
self.call(CmdTest(), "1 obj", "Number: 1 Args: obj")
|
||||
|
||||
def test_mux_command(self):
|
||||
class CmdTest(MuxCommand):
|
||||
key = "test"
|
||||
|
|
|
|||
|
|
@ -155,6 +155,22 @@ class Component(metaclass=BaseComponent):
|
|||
"""
|
||||
return self.host.attributes
|
||||
|
||||
@property
|
||||
def pk(self):
|
||||
"""
|
||||
Shortcut property returning the host's primary key.
|
||||
|
||||
Returns:
|
||||
int: The Host's primary key.
|
||||
|
||||
Notes:
|
||||
This is requried to allow AttributeProperties to correctly update `_SaverMutable` data
|
||||
(like lists) in-place (since the DBField sits on the Component which doesn't itself
|
||||
have a primary key, this save operation would otherwise fail).
|
||||
|
||||
"""
|
||||
return self.host.pk
|
||||
|
||||
@property
|
||||
def nattributes(self):
|
||||
"""
|
||||
|
|
|
|||
|
|
@ -6,7 +6,8 @@ This file contains the Descriptors used to set Fields in Components
|
|||
|
||||
import typing
|
||||
|
||||
from evennia.typeclasses.attributes import AttributeProperty, NAttributeProperty
|
||||
from evennia.typeclasses.attributes import (AttributeProperty,
|
||||
NAttributeProperty)
|
||||
|
||||
if typing.TYPE_CHECKING:
|
||||
from .components import Component
|
||||
|
|
|
|||
|
|
@ -268,7 +268,7 @@ class TestComponents(EvenniaTest):
|
|||
|
||||
def test_mutables_are_not_shared_when_autocreate(self):
|
||||
self.char1.test_a.my_list.append(1)
|
||||
self.assertNotEqual(self.char1.test_a.my_list, self.char2.test_a.my_list)
|
||||
self.assertIsNot(self.char1.test_a.my_list, self.char2.test_a.my_list)
|
||||
|
||||
def test_replacing_class_component_slot_with_runtime_component(self):
|
||||
self.char1.components.add_default("replacement_inherited_test_a")
|
||||
|
|
|
|||
|
|
@ -20,11 +20,11 @@ import uuid
|
|||
from collections import defaultdict
|
||||
|
||||
from django.core import exceptions as django_exceptions
|
||||
|
||||
from evennia.prototypes import spawner
|
||||
from evennia.utils.utils import class_from_module
|
||||
|
||||
from .utils import (BIGVAL, MAPSCAN, REVERSE_DIRECTIONS, MapError,
|
||||
MapParserError)
|
||||
from .utils import BIGVAL, MAPSCAN, REVERSE_DIRECTIONS, MapError, MapParserError
|
||||
|
||||
NodeTypeclass = None
|
||||
ExitTypeclass = None
|
||||
|
|
@ -331,7 +331,8 @@ class MapNode:
|
|||
raise MapError(
|
||||
f"Multiple objects found: {NodeTypeclass.objects.filter_xyz(xyz=xyz)}. "
|
||||
"This may be due to manual creation of XYZRooms at this position. "
|
||||
"Delete duplicates.", self
|
||||
"Delete duplicates.",
|
||||
self,
|
||||
)
|
||||
else:
|
||||
self.log(f" updating existing room (if changed) at xyz={xyz}")
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ used as stand-alone XYZ-coordinate-aware rooms.
|
|||
|
||||
from django.conf import settings
|
||||
from django.db.models import Q
|
||||
|
||||
from evennia.objects.manager import ObjectManager
|
||||
from evennia.objects.objects import DefaultExit, DefaultRoom
|
||||
|
||||
|
|
@ -282,7 +283,7 @@ class XYZRoom(DefaultRoom):
|
|||
|
||||
def __repr__(self):
|
||||
x, y, z = self.xyz
|
||||
return f"<XYZRoom '{self.db_key}', XYZ=({x},{y},{z})>"
|
||||
return f"<{self.__class__.__name__} '{self.db_key}', XYZ=({x},{y},{z})>"
|
||||
|
||||
@property
|
||||
def xyz(self):
|
||||
|
|
@ -307,8 +308,7 @@ class XYZRoom(DefaultRoom):
|
|||
def xyzgrid(self):
|
||||
global GET_XYZGRID
|
||||
if not GET_XYZGRID:
|
||||
from evennia.contrib.grid.xyzgrid.xyzgrid import \
|
||||
get_xyzgrid as GET_XYZGRID
|
||||
from evennia.contrib.grid.xyzgrid.xyzgrid import get_xyzgrid as GET_XYZGRID
|
||||
return GET_XYZGRID()
|
||||
|
||||
@property
|
||||
|
|
@ -532,8 +532,7 @@ class XYZExit(DefaultExit):
|
|||
def xyzgrid(self):
|
||||
global GET_XYZGRID
|
||||
if not GET_XYZGRID:
|
||||
from evennia.contrib.grid.xyzgrid.xyzgrid import \
|
||||
get_xyzgrid as GET_XYZGRID
|
||||
from evennia.contrib.grid.xyzgrid.xyzgrid import get_xyzgrid as GET_XYZGRID
|
||||
return GET_XYZGRID()
|
||||
|
||||
@property
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ EvAdventure character generation.
|
|||
"""
|
||||
|
||||
from django.conf import settings
|
||||
|
||||
from evennia.objects.models import ObjectDB
|
||||
from evennia.prototypes.spawner import spawn
|
||||
from evennia.utils.create import create_object
|
||||
|
|
|
|||
|
|
@ -10,10 +10,11 @@ import time
|
|||
import typing
|
||||
from collections import defaultdict
|
||||
|
||||
import evennia
|
||||
import inflect
|
||||
from django.conf import settings
|
||||
from django.utils.translation import gettext as _
|
||||
|
||||
import evennia
|
||||
from evennia.commands import cmdset
|
||||
from evennia.commands.cmdsethandler import CmdSetHandler
|
||||
from evennia.objects.manager import ObjectManager
|
||||
|
|
@ -23,9 +24,17 @@ from evennia.server.signals import SIGNAL_EXIT_TRAVERSED
|
|||
from evennia.typeclasses.attributes import ModelAttributeBackend, NickHandler
|
||||
from evennia.typeclasses.models import TypeclassBase
|
||||
from evennia.utils import ansi, create, funcparser, logger, search
|
||||
from evennia.utils.utils import (class_from_module, compress_whitespace, dbref,
|
||||
is_iter, iter_to_str, lazy_property,
|
||||
make_iter, to_str, variable_from_module)
|
||||
from evennia.utils.utils import (
|
||||
class_from_module,
|
||||
compress_whitespace,
|
||||
dbref,
|
||||
is_iter,
|
||||
iter_to_str,
|
||||
lazy_property,
|
||||
make_iter,
|
||||
to_str,
|
||||
variable_from_module,
|
||||
)
|
||||
|
||||
_INFLECT = inflect.engine()
|
||||
_MULTISESSION_MODE = settings.MULTISESSION_MODE
|
||||
|
|
@ -1425,7 +1434,8 @@ class DefaultObject(ObjectDB, metaclass=TypeclassBase):
|
|||
return [
|
||||
obj
|
||||
for obj in obj_list
|
||||
if obj != looker and (obj.access(looker, "view") and obj.access(looker, "search", default=True))
|
||||
if obj != looker
|
||||
and (obj.access(looker, "view") and obj.access(looker, "search", default=True))
|
||||
]
|
||||
|
||||
# name and return_appearance hooks
|
||||
|
|
@ -1563,12 +1573,34 @@ class DefaultObject(ObjectDB, metaclass=TypeclassBase):
|
|||
Args:
|
||||
looker (DefaultObject): Object doing the looking.
|
||||
**kwargs: Arbitrary data for use when overriding.
|
||||
|
||||
Keyword Args:
|
||||
exit_order (iterable of str): The order in which exits should be listed, with
|
||||
unspecified exits appearing at the end, alphabetically.
|
||||
|
||||
Returns:
|
||||
str: The exits display data.
|
||||
|
||||
Examples:
|
||||
::
|
||||
|
||||
For a room with exits in the order 'portal', 'south', 'north', and 'out':
|
||||
obj.get_display_name(looker, exit_order=('north', 'south'))
|
||||
-> "Exits: north, south, out, and portal." (markup not shown here)
|
||||
"""
|
||||
def _sort_exit_names(names):
|
||||
exit_order = kwargs.get("exit_order")
|
||||
if not exit_order:
|
||||
return names
|
||||
sort_index = {name: key for key, name in enumerate(exit_order)}
|
||||
names = sorted(names)
|
||||
end_pos = len(names) + 1
|
||||
names.sort(key=lambda name:sort_index.get(name, end_pos))
|
||||
return names
|
||||
|
||||
exits = self.filter_visible(self.contents_get(content_type="exit"), looker, **kwargs)
|
||||
exit_names = iter_to_str(exi.get_display_name(looker, **kwargs) for exi in exits)
|
||||
exit_names = (exi.get_display_name(looker, **kwargs) for exi in exits)
|
||||
exit_names = iter_to_str(_sort_exit_names(exit_names))
|
||||
|
||||
return f"|wExits:|n {exit_names}" if exit_names else ""
|
||||
|
||||
|
|
|
|||
|
|
@ -3,13 +3,10 @@ from unittest import skip
|
|||
from evennia import DefaultCharacter, DefaultExit, DefaultObject, DefaultRoom
|
||||
from evennia.objects.models import ObjectDB
|
||||
from evennia.typeclasses.attributes import AttributeProperty
|
||||
from evennia.typeclasses.tags import (
|
||||
AliasProperty,
|
||||
PermissionProperty,
|
||||
TagCategoryProperty,
|
||||
TagProperty,
|
||||
)
|
||||
from evennia.typeclasses.tags import (AliasProperty, PermissionProperty,
|
||||
TagCategoryProperty, TagProperty)
|
||||
from evennia.utils import create, search
|
||||
from evennia.utils.ansi import strip_ansi
|
||||
from evennia.utils.test_resources import BaseEvenniaTest, EvenniaTestCase
|
||||
|
||||
|
||||
|
|
@ -94,6 +91,21 @@ class DefaultObjectTest(BaseEvenniaTest):
|
|||
all_return_exit = ex1.get_return_exit(return_all=True)
|
||||
self.assertEqual(len(all_return_exit), 2)
|
||||
|
||||
def test_exit_order(self):
|
||||
DefaultExit.create("south", self.room1, self.room2, account=self.account)
|
||||
DefaultExit.create("portal", self.room1, self.room2, account=self.account)
|
||||
DefaultExit.create("north", self.room1, self.room2, account=self.account)
|
||||
DefaultExit.create("aperture", self.room1, self.room2, account=self.account)
|
||||
|
||||
# in creation order
|
||||
exits = strip_ansi(self.room1.get_display_exits(self.char1))
|
||||
self.assertEqual(exits, "Exits: out, south, portal, north, and aperture")
|
||||
|
||||
# in specified order with unspecified exits alpbabetically on the end
|
||||
exit_order = ('north', 'south', 'out')
|
||||
exits = strip_ansi(self.room1.get_display_exits(self.char1, exit_order=exit_order))
|
||||
self.assertEqual(exits, "Exits: north, south, out, aperture, and portal")
|
||||
|
||||
def test_urls(self):
|
||||
"Make sure objects are returning URLs"
|
||||
self.assertTrue(self.char1.get_absolute_url())
|
||||
|
|
@ -356,6 +368,10 @@ class TestObjectPropertiesClass(DefaultObject):
|
|||
attr2 = AttributeProperty(default="attr2", category="attrcategory")
|
||||
attr3 = AttributeProperty(default="attr3", autocreate=False)
|
||||
attr4 = SubAttributeProperty(default="attr4")
|
||||
attr5 = AttributeProperty(default=list, autocreate=False)
|
||||
attr6 = AttributeProperty(default=[None], autocreate=False)
|
||||
attr7 = AttributeProperty(default=list)
|
||||
attr8 = AttributeProperty(default=[None])
|
||||
cusattr = CustomizedProperty(default=5)
|
||||
tag1 = TagProperty()
|
||||
tag2 = TagProperty(category="tagcategory")
|
||||
|
|
@ -541,3 +557,99 @@ class TestProperties(EvenniaTestCase):
|
|||
|
||||
obj1.delete()
|
||||
obj2.delete()
|
||||
|
||||
def test_not_create_attribute_with_autocreate_false(self):
|
||||
"""
|
||||
Test that AttributeProperty with autocreate=False does not create an attribute in the database.
|
||||
|
||||
"""
|
||||
obj = create.create_object(TestObjectPropertiesClass, key="obj1")
|
||||
|
||||
self.assertEqual(obj.attr3, "attr3")
|
||||
self.assertEqual(obj.attributes.get("attr3"), None)
|
||||
|
||||
self.assertEqual(obj.attr5, [])
|
||||
self.assertEqual(obj.attributes.get("attr5"), None)
|
||||
|
||||
obj.delete()
|
||||
|
||||
def test_callable_defaults__autocreate_false(self):
|
||||
"""
|
||||
Test https://github.com/evennia/evennia/issues/3488, where a callable default value like `list`
|
||||
would produce an infinitely empty result even when appended to.
|
||||
|
||||
"""
|
||||
obj1 = create.create_object(TestObjectPropertiesClass, key="obj1")
|
||||
obj2 = create.create_object(TestObjectPropertiesClass, key="obj2")
|
||||
|
||||
self.assertEqual(obj1.attr5, [])
|
||||
obj1.attr5.append(1)
|
||||
self.assertEqual(obj1.attr5, [1])
|
||||
|
||||
# check cross-instance sharing
|
||||
self.assertEqual(obj2.attr5, [], "cross-instance sharing detected")
|
||||
|
||||
|
||||
def test_mutable_defaults__autocreate_false(self):
|
||||
"""
|
||||
Test https://github.com/evennia/evennia/issues/3488, where a mutable default value (like a
|
||||
list `[]` or `[None]`) would not be updated in the database when appended to.
|
||||
|
||||
Note that using a mutable default value is not recommended, as the mutable will share the
|
||||
same memory space across all instances of the class. This means that if one instance modifiesA
|
||||
the mutable, all instances will be affected.
|
||||
|
||||
"""
|
||||
obj1 = create.create_object(TestObjectPropertiesClass, key="obj1")
|
||||
obj2 = create.create_object(TestObjectPropertiesClass, key="obj2")
|
||||
|
||||
self.assertEqual(obj1.attr6, [None])
|
||||
obj1.attr6.append(1)
|
||||
self.assertEqual(obj1.attr6, [None, 1])
|
||||
|
||||
obj1.attr6[1] = 2
|
||||
self.assertEqual(obj1.attr6, [None, 2])
|
||||
|
||||
# check cross-instance sharing
|
||||
self.assertEqual(obj2.attr6, [None], "cross-instance sharing detected")
|
||||
|
||||
obj1.delete()
|
||||
obj2.delete()
|
||||
|
||||
def test_callable_defaults__autocreate_true(self):
|
||||
"""
|
||||
Test callables with autocreate=True.
|
||||
|
||||
"""
|
||||
obj1 = create.create_object(TestObjectPropertiesClass, key="obj1")
|
||||
obj2 = create.create_object(TestObjectPropertiesClass, key="obj1")
|
||||
|
||||
self.assertEqual(obj1.attr7, [])
|
||||
obj1.attr7.append(1)
|
||||
self.assertEqual(obj1.attr7, [1])
|
||||
|
||||
# check cross-instance sharing
|
||||
self.assertEqual(obj2.attr7, [])
|
||||
|
||||
|
||||
def test_mutable_defaults__autocreate_true(self):
|
||||
"""
|
||||
Test mutable defaults with autocreate=True.
|
||||
|
||||
"""
|
||||
obj1 = create.create_object(TestObjectPropertiesClass, key="obj1")
|
||||
obj2 = create.create_object(TestObjectPropertiesClass, key="obj2")
|
||||
|
||||
self.assertEqual(obj1.attr8, [None])
|
||||
obj1.attr8.append(1)
|
||||
self.assertEqual(obj1.attr8, [None, 1])
|
||||
|
||||
obj1.attr8[1] = 2
|
||||
self.assertEqual(obj1.attr8, [None, 2])
|
||||
|
||||
# check cross-instance sharing
|
||||
self.assertEqual(obj2.attr8, [None])
|
||||
|
||||
obj1.delete()
|
||||
obj2.delete()
|
||||
|
||||
|
|
|
|||
|
|
@ -642,6 +642,8 @@ def send_instruction(operation, arguments, callback=None, errback=None):
|
|||
"""
|
||||
global AMP_CONNECTION, REACTOR_RUN
|
||||
|
||||
# print("launcher: Sending to portal: {} + {}".format(ord(operation), arguments))
|
||||
|
||||
if None in (AMP_HOST, AMP_PORT, AMP_INTERFACE):
|
||||
print(ERROR_AMP_UNCONFIGURED)
|
||||
sys.exit()
|
||||
|
|
|
|||
|
|
@ -197,8 +197,6 @@ class AMPServerProtocol(amp.AMPMultiConnectionProtocol):
|
|||
if process and not _is_windows():
|
||||
# avoid zombie-process on Unix/BSD
|
||||
process.wait()
|
||||
# unset the reset-mode flag on the portal
|
||||
self.factory.portal.server_restart_mode = None
|
||||
return
|
||||
|
||||
def wait_for_disconnect(self, callback, *args, **kwargs):
|
||||
|
|
@ -232,11 +230,18 @@ class AMPServerProtocol(amp.AMPMultiConnectionProtocol):
|
|||
|
||||
"""
|
||||
if mode == "reload":
|
||||
self.send_AdminPortal2Server(amp.DUMMYSESSION, operation=amp.SRELOAD)
|
||||
self.send_AdminPortal2Server(
|
||||
amp.DUMMYSESSION, operation=amp.SRELOAD, server_restart_mode=mode
|
||||
)
|
||||
elif mode == "reset":
|
||||
self.send_AdminPortal2Server(amp.DUMMYSESSION, operation=amp.SRESET)
|
||||
self.send_AdminPortal2Server(
|
||||
amp.DUMMYSESSION, operation=amp.SRESET, server_restart_mode=mode
|
||||
)
|
||||
elif mode == "shutdown":
|
||||
self.send_AdminPortal2Server(amp.DUMMYSESSION, operation=amp.SSHUTD)
|
||||
self.send_AdminPortal2Server(
|
||||
amp.DUMMYSESSION, operation=amp.SSHUTD, server_restart_mode=mode
|
||||
)
|
||||
# store the mode for use once server comes back up again
|
||||
self.factory.portal.server_restart_mode = mode
|
||||
|
||||
# sending amp data
|
||||
|
|
@ -326,7 +331,6 @@ class AMPServerProtocol(amp.AMPMultiConnectionProtocol):
|
|||
_, server_connected, _, _, _, _ = self.get_status()
|
||||
|
||||
# logger.log_msg("Evennia Launcher->Portal operation %s:%s received" % (ord(operation), arguments))
|
||||
|
||||
# logger.log_msg("operation == amp.SSTART: {}: {}".format(operation == amp.SSTART, amp.loads(arguments)))
|
||||
|
||||
if operation == amp.SSTART: # portal start #15
|
||||
|
|
@ -405,11 +409,11 @@ class AMPServerProtocol(amp.AMPMultiConnectionProtocol):
|
|||
|
||||
sessid, kwargs = self.data_in(packed_data)
|
||||
|
||||
# logger.log_msg("Evennia Server->Portal admin data %s:%s received" % (sessid, kwargs))
|
||||
|
||||
operation = kwargs.pop("operation")
|
||||
portal_sessionhandler = evennia.PORTAL_SESSION_HANDLER
|
||||
|
||||
# logger.log_msg(f"Evennia Server->Portal admin data operation {ord(operation)}")
|
||||
|
||||
if operation == amp.SLOGIN: # server_session_login
|
||||
# a session has authenticated; sync it.
|
||||
session = portal_sessionhandler.get(sessid)
|
||||
|
|
@ -427,22 +431,28 @@ class AMPServerProtocol(amp.AMPMultiConnectionProtocol):
|
|||
portal_sessionhandler.server_disconnect_all(reason=kwargs.get("reason"))
|
||||
|
||||
elif operation == amp.SRELOAD: # server reload
|
||||
# set up callback to restart server once it has disconnected
|
||||
self.factory.server_connection.wait_for_disconnect(
|
||||
self.start_server, self.factory.portal.server_twistd_cmd
|
||||
)
|
||||
# tell server to reload
|
||||
self.stop_server(mode="reload")
|
||||
|
||||
elif operation == amp.SRESET: # server reset
|
||||
# set up callback to restart server once it has disconnected
|
||||
self.factory.server_connection.wait_for_disconnect(
|
||||
self.start_server, self.factory.portal.server_twistd_cmd
|
||||
)
|
||||
# tell server to reset
|
||||
self.stop_server(mode="reset")
|
||||
|
||||
elif operation == amp.SSHUTD: # server-only shutdown
|
||||
self.stop_server(mode="shutdown")
|
||||
|
||||
elif operation == amp.PSHUTD: # full server+server shutdown
|
||||
# set up callback to shut down portal once server has disconnected
|
||||
self.factory.server_connection.wait_for_disconnect(self.factory.portal.shutdown)
|
||||
# tell server to shut down
|
||||
self.stop_server(mode="shutdown")
|
||||
|
||||
elif operation == amp.PSYNC: # portal sync
|
||||
|
|
@ -451,6 +461,7 @@ class AMPServerProtocol(amp.AMPMultiConnectionProtocol):
|
|||
self.factory.portal.server_process_id = kwargs.get("spid", None)
|
||||
# this defaults to 'shutdown' or whatever value set in server_stop
|
||||
server_restart_mode = self.factory.portal.server_restart_mode
|
||||
# print("Server has connected. Sending session data to Server ... mode: {}".format(server_restart_mode))
|
||||
|
||||
sessdata = evennia.PORTAL_SESSION_HANDLER.get_all_sync_data()
|
||||
self.send_AdminPortal2Server(
|
||||
|
|
@ -461,6 +472,7 @@ class AMPServerProtocol(amp.AMPMultiConnectionProtocol):
|
|||
portal_start_time=self.factory.portal.start_time,
|
||||
)
|
||||
evennia.PORTAL_SESSION_HANDLER.at_server_connection()
|
||||
self.factory.portal.server_restart_mode = None
|
||||
|
||||
if self.factory.server_connection:
|
||||
# this is an indication the server has successfully connected, so
|
||||
|
|
@ -480,7 +492,7 @@ class AMPServerProtocol(amp.AMPMultiConnectionProtocol):
|
|||
)
|
||||
|
||||
# set a flag in case we are about to shut down soon
|
||||
self.factory.server_restart_mode = True
|
||||
self.factory.server_restart_mode = "shutdown"
|
||||
|
||||
elif operation == amp.SCONN: # server_force_connection (for irc/etc)
|
||||
portal_sessionhandler.server_connect(**kwargs)
|
||||
|
|
|
|||
|
|
@ -12,11 +12,11 @@ which is a non-db version of Attributes.
|
|||
import fnmatch
|
||||
import re
|
||||
from collections import defaultdict
|
||||
from copy import copy
|
||||
|
||||
from django.conf import settings
|
||||
from django.db import models
|
||||
from django.utils.encoding import smart_str
|
||||
|
||||
from evennia.locks.lockhandler import LockHandler
|
||||
from evennia.utils.dbserialize import from_pickle, to_pickle
|
||||
from evennia.utils.idmapper.models import SharedMemoryModel
|
||||
|
|
@ -166,6 +166,7 @@ class AttributeProperty:
|
|||
"""
|
||||
|
||||
attrhandler_name = "attributes"
|
||||
cached_default_name_template = "_property_attribute_default_{key}"
|
||||
|
||||
def __init__(self, default=None, category=None, strattr=False, lockstring="", autocreate=True):
|
||||
"""
|
||||
|
|
@ -207,21 +208,6 @@ class AttributeProperty:
|
|||
self._autocreate = autocreate
|
||||
self._key = ""
|
||||
|
||||
@property
|
||||
def _default(self):
|
||||
"""
|
||||
Tries returning a new instance of default if callable.
|
||||
|
||||
"""
|
||||
if callable(self.__default):
|
||||
return self.__default()
|
||||
|
||||
return self.__default
|
||||
|
||||
@_default.setter
|
||||
def _default(self, value):
|
||||
self.__default = value
|
||||
|
||||
def __set_name__(self, cls, name):
|
||||
"""
|
||||
Called when descriptor is first assigned to the class. It is called with
|
||||
|
|
@ -230,17 +216,35 @@ class AttributeProperty:
|
|||
"""
|
||||
self._key = name
|
||||
|
||||
def _get_and_cache_default(self, instance):
|
||||
"""
|
||||
Get and cache the default value for this attribute. We make sure to convert any mutables
|
||||
into _Saver* equivalent classes here and cache the result on the instance's AttributeHandler.
|
||||
|
||||
"""
|
||||
attrhandler = getattr(instance, self.attrhandler_name)
|
||||
value = getattr(attrhandler, self.cached_default_name_template.format(key=self._key), None)
|
||||
if not value:
|
||||
if callable(self._default):
|
||||
value = self._default()
|
||||
else:
|
||||
value = copy(self._default)
|
||||
value = from_pickle(value, db_obj=instance)
|
||||
setattr(attrhandler, self.cached_default_name_template.format(key=self._key), value)
|
||||
return value
|
||||
|
||||
def __get__(self, instance, owner):
|
||||
"""
|
||||
Called when the attrkey is retrieved from the instance.
|
||||
|
||||
"""
|
||||
value = self._default
|
||||
value = self._get_and_cache_default(instance)
|
||||
|
||||
try:
|
||||
value = self.at_get(
|
||||
getattr(instance, self.attrhandler_name).get(
|
||||
key=self._key,
|
||||
default=self._default,
|
||||
default=value,
|
||||
category=self._category,
|
||||
strattr=self._strattr,
|
||||
raise_exception=self._autocreate,
|
||||
|
|
@ -250,7 +254,7 @@ class AttributeProperty:
|
|||
except AttributeError:
|
||||
if self._autocreate:
|
||||
# attribute didn't exist and autocreate is set
|
||||
self.__set__(instance, self._default)
|
||||
self.__set__(instance, value)
|
||||
else:
|
||||
raise
|
||||
return value
|
||||
|
|
|
|||
|
|
@ -98,7 +98,7 @@ _HELP_TEXT = _(
|
|||
:s <l> <w> <txt> - search/replace word or regex <w> in buffer or on line <l>
|
||||
|
||||
:j <l> <w> - justify buffer or line <l>. <w> is f, c, l or r. Default f (full)
|
||||
:f <l> - flood-fill entire buffer or line <l>: Equivalent to :j left
|
||||
:f <l> - flood-fill entire buffer or line <l>. Equivalent to :j <l> l
|
||||
:fi <l> - indent entire buffer or line <l>
|
||||
:fd <l> - de-indent entire buffer or line <l>
|
||||
|
||||
|
|
@ -351,6 +351,35 @@ class CmdEditorBase(_COMMAND_DEFAULT_CLASS):
|
|||
self.arg1 = arg1
|
||||
self.arg2 = arg2
|
||||
|
||||
def insert_raw_string_into_buffer(self):
|
||||
"""
|
||||
Insert a line into the buffer. Used by both CmdLineInput and CmdEditorGroup.
|
||||
|
||||
"""
|
||||
caller = self.caller
|
||||
editor = caller.ndb._eveditor
|
||||
buf = editor.get_buffer()
|
||||
|
||||
# add a line of text to buffer
|
||||
line = self.raw_string.strip("\r\n")
|
||||
if editor._codefunc and editor._indent >= 0:
|
||||
# if automatic indentation is active, add spaces
|
||||
line = editor.deduce_indent(line, buf)
|
||||
buf = line if not buf else buf + "\n%s" % line
|
||||
self.editor.update_buffer(buf)
|
||||
if self.editor._echo_mode:
|
||||
# need to do it here or we will be off one line
|
||||
cline = len(self.editor.get_buffer().split("\n"))
|
||||
if editor._codefunc:
|
||||
# display the current level of identation
|
||||
indent = editor._indent
|
||||
if indent < 0:
|
||||
indent = "off"
|
||||
|
||||
self.caller.msg("|b%02i|||n (|g%s|n) %s" % (cline, indent, raw(line)))
|
||||
else:
|
||||
self.caller.msg("|b%02i|||n %s" % (cline, raw(line)))
|
||||
|
||||
|
||||
def _load_editor(caller):
|
||||
"""
|
||||
|
|
@ -394,29 +423,7 @@ class CmdLineInput(CmdEditorBase):
|
|||
If the editor handles code, it might add automatic
|
||||
indentation.
|
||||
"""
|
||||
caller = self.caller
|
||||
editor = caller.ndb._eveditor
|
||||
buf = editor.get_buffer()
|
||||
|
||||
# add a line of text to buffer
|
||||
line = self.raw_string.strip("\r\n")
|
||||
if editor._codefunc and editor._indent >= 0:
|
||||
# if automatic indentation is active, add spaces
|
||||
line = editor.deduce_indent(line, buf)
|
||||
buf = line if not buf else buf + "\n%s" % line
|
||||
self.editor.update_buffer(buf)
|
||||
if self.editor._echo_mode:
|
||||
# need to do it here or we will be off one line
|
||||
cline = len(self.editor.get_buffer().split("\n"))
|
||||
if editor._codefunc:
|
||||
# display the current level of identation
|
||||
indent = editor._indent
|
||||
if indent < 0:
|
||||
indent = "off"
|
||||
|
||||
self.caller.msg("|b%02i|||n (|g%s|n) %s" % (cline, indent, raw(line)))
|
||||
else:
|
||||
self.caller.msg("|b%02i|||n %s" % (cline, raw(line)))
|
||||
self.insert_raw_string_into_buffer()
|
||||
|
||||
|
||||
class CmdEditorGroup(CmdEditorBase):
|
||||
|
|
@ -806,6 +813,9 @@ class CmdEditorGroup(CmdEditorBase):
|
|||
caller.msg(_("Auto-indentation turned off."))
|
||||
else:
|
||||
caller.msg(_("This command is only available in code editor mode."))
|
||||
else:
|
||||
# no match - insert as line in buffer
|
||||
self.insert_raw_string_into_buffer()
|
||||
|
||||
|
||||
class EvEditorCmdSet(CmdSet):
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue