Merge pull request #2904 from Henddher/issue_2695

fix: xyzgrid spawn doesn't call at_object_creation
This commit is contained in:
Griatch 2022-10-19 22:29:35 +02:00 committed by GitHub
commit 4ba7df7f61
3 changed files with 105 additions and 24 deletions

View file

@ -53,28 +53,31 @@ Exits: northeast and east
(then go back to your mygame/ folder)
This will install all optional requirements of Evennia.
2. Import and add the `evennia.contrib.commands.XYZGridCmdSet` to the
2. Import and [add] the `evennia.contrib.grid.xyzgrid.commands.XYZGridCmdSet` to the
`CharacterCmdset` cmdset in `mygame/commands.default_cmds.py`. Reload
the server. This makes the `map`, `goto/path` and the modified `teleport` and
`open` commands available in-game.
[add]: docs/source/Command-Sets.md#defining-command-sets
3. Edit `mygame/server/conf/settings.py` and add
EXTRA_LAUNCHER_COMMANDS['xyzgrid'] = 'evennia.contrib.launchcmd.xyzcommand'
and
PROTOTYPE_MODULES += [evennia.contrib.grid.xyzgrid.prototypes]
EXTRA_LAUNCHER_COMMANDS['xyzgrid'] = 'evennia.contrib.grid.xyzgrid.launchcmd.xyzcommand'
PROTOTYPE_MODULES += ['evennia.contrib.grid.xyzgrid.prototypes']
This will add the new ability to enter `evennia xyzgrid <option>` on the
command line. It will also make the `xyz_room` and `xyz_exit` prototypes
available for use as prototype-parents when spawning the grid.
4. Run `evennia xyzgrid help` for available options.
5. (Optional): By default, the xyzgrid will only spawn module-based
[prototypes](../Components/Prototypes.md). This is an optimization and usually makes sense
[prototypes]. This is an optimization and usually makes sense
since the grid is entirely defined outside the game anyway. If you want to
also make use of in-game (db-) created prototypes, add
`XYZGRID_USE_DB_PROTOTYPES = True` to settings.
[prototypes]: ../Components/Prototypes.md
## Overview

View file

@ -3,6 +3,7 @@
Tests for the XYZgrid system.
"""
from unittest import mock
from random import randint
from parameterized import parameterized
@ -1415,3 +1416,78 @@ class TestBuildExampleGrid(BaseEvenniaTest):
self.assertTrue(room2a.db.desc.startswith("This is the entrance to"))
self.assertEqual(room2b.key, "North-west corner of the atrium")
self.assertTrue(room2b.db.desc.startswith("Sunlight sifts down"))
mock_room_callbacks = mock.MagicMock()
mock_exit_callbacks = mock.MagicMock()
class TestXyzRoom(xyzroom.XYZRoom):
def at_object_creation(self):
mock_room_callbacks.at_object_creation()
class TestXyzExit(xyzroom.XYZExit):
def at_object_creation(self):
mock_exit_callbacks.at_object_creation()
MAP_DATA = {
"map": """
+ 0 1
0 #-#
+ 0 1
""",
"zcoord": "map1",
"prototypes": {
("*", "*"): {
"key": "room",
"desc": "A room.",
"prototype_parent": "xyz_room",
},
("*", "*", "*"): {
"desc": "A passage.",
"prototype_parent": "xyz_exit",
}
},
"options": {
"map_visual_range": 1,
"map_mode": "scan",
}
}
class TestCallbacks(BaseEvenniaTest):
def setUp(self):
super().setUp()
mock_room_callbacks.reset_mock()
mock_exit_callbacks.reset_mock()
def setup_grid(self, map_data):
self.grid, err = xyzgrid.XYZGrid.create("testgrid")
def _log(msg):
print(msg)
self.grid.log = _log
self.map_data = map_data
self.grid.add_maps(map_data)
def tearDown(self):
super().tearDown()
self.grid.delete()
def test_typeclassed_xyzroom_and_xyzexit_with_at_object_creation_are_called(self):
map_data = dict(MAP_DATA)
for prototype_key, prototype_value in map_data["prototypes"].items():
if len(prototype_key) == 2:
prototype_value["typeclass"] = "evennia.contrib.grid.xyzgrid.tests.TestXyzRoom"
if len(prototype_key) == 3:
prototype_value["typeclass"] = "evennia.contrib.grid.xyzgrid.tests.TestXyzExit"
self.setup_grid(map_data)
self.grid.spawn()
# Two rooms and 2 exits, Each one should have gotten one `at_object_creation` callback.
self.assertEqual(mock_room_callbacks.at_object_creation.mock_calls, [mock.call(), mock.call()])
self.assertEqual(mock_exit_callbacks.at_object_creation.mock_calls, [mock.call(), mock.call()])

View file

@ -22,8 +22,9 @@ 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 MAPSCAN, REVERSE_DIRECTIONS, MapParserError, BIGVAL
from .utils import MAPSCAN, REVERSE_DIRECTIONS, MapParserError, BIGVAL, MapError
NodeTypeclass = None
ExitTypeclass = None
@ -316,13 +317,14 @@ class MapNode:
try:
nodeobj = NodeTypeclass.objects.get_xyz(xyz=xyz)
except django_exceptions.ObjectDoesNotExist:
# create a new entity with proper coordinates etc
tclass = self.prototype["typeclass"]
tclass = (
f" ({tclass})" if tclass != "evennia.contrib.grid.xyzgrid.xyzroom.XYZRoom" else ""
)
self.log(f" spawning room at xyz={xyz}{tclass}")
nodeobj, err = NodeTypeclass.create(self.prototype.get("key", "An empty room"), xyz=xyz)
# create a new entity, using the specified typeclass (if there's one) and
# with proper coordinates etc
typeclass = self.prototype.get("typeclass")
if typeclass is None:
raise MapError(f"The prototype {self.prototype} for this node has no 'typeclass' key.", self)
self.log(f" spawning room at xyz={xyz} ({typeclass})")
Typeclass = class_from_module(typeclass)
nodeobj, err = Typeclass.create(self.prototype.get("key", "An empty room"), xyz=xyz)
if err:
raise RuntimeError(err)
else:
@ -400,7 +402,14 @@ class MapNode:
continue
exitnode = self.links[direction]
exi, err = ExitTypeclass.create(
prot = maplinks[key.lower()][3].prototype
typeclass = prot.get("typeclass")
if typeclass is None:
raise MapError(f"The prototype {self.prototype} for this node has no 'typeclass' key.", self)
self.log(f" spawning/updating exit xyz={xyz}, direction={key} ({typeclass})")
Typeclass = class_from_module(typeclass)
exi, err = Typeclass.create(
key,
xyz=xyz,
xyz_destination=exitnode.get_spawn_xyz(),
@ -408,15 +417,8 @@ class MapNode:
)
if err:
raise RuntimeError(err)
linkobjs[key.lower()] = exi
prot = maplinks[key.lower()][3].prototype
tclass = prot["typeclass"]
tclass = (
f" ({tclass})"
if tclass != "evennia.contrib.grid.xyzgrid.xyzroom.XYZExit"
else ""
)
self.log(f" spawning/updating exit xyz={xyz}, direction={key}{tclass}")
# apply prototypes to catch any changes
for key, linkobj in linkobjs.items():