Fixing contrib test issues
This commit is contained in:
parent
04a95297b5
commit
7f2b8c81d7
30 changed files with 248 additions and 565 deletions
|
|
@ -59,7 +59,7 @@ Exits: northeast and east
|
||||||
|
|
||||||
and
|
and
|
||||||
|
|
||||||
PROTOTYPE_MODULES += [’evennia.contrib.xyzgrid.prototypes’]
|
PROTOTYPE_MODULES += [’evennia.contrib.grid.xyzgrid.prototypes’]
|
||||||
|
|
||||||
This will add the new ability to enter `evennia xyzgrid <option>` on the
|
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
|
command line. It will also make the `xyz_room` and `xyz_exit` prototypes
|
||||||
|
|
@ -107,7 +107,7 @@ The `evennia xyzgrid` is a custom launch option added only for this contrib.
|
||||||
|
|
||||||
The xyzgrid-contrib comes with a full grid example. Let's add it:
|
The xyzgrid-contrib comes with a full grid example. Let's add it:
|
||||||
|
|
||||||
$ evennia xyzgrid add evennia.contrib.xyzgrid.example
|
$ evennia xyzgrid add evennia.contrib.grid.xyzgrid.example
|
||||||
|
|
||||||
You can now list the maps on your grid:
|
You can now list the maps on your grid:
|
||||||
|
|
||||||
|
|
@ -120,7 +120,7 @@ about each map with the `show` subcommand:
|
||||||
$ evennia xyzgrid show "the small cave"
|
$ evennia xyzgrid show "the small cave"
|
||||||
|
|
||||||
If you want to peek at how the grid's code, open
|
If you want to peek at how the grid's code, open
|
||||||
[evennia/contrib/xyzgrid/example.py](evennia.contrib.xyzgrid.example).
|
[evennia/contrib/grid/xyzgrid/example.py](evennia.contrib.xyzgrid.example).
|
||||||
(We'll explain the details in later sections).
|
(We'll explain the details in later sections).
|
||||||
|
|
||||||
So far the grid is 'abstract' and has no actual in-game presence. Let's
|
So far the grid is 'abstract' and has no actual in-game presence. Let's
|
||||||
|
|
@ -430,9 +430,9 @@ LEGEND = {
|
||||||
The legend is optional, and any symbol not explicitly given in your legend will
|
The legend is optional, and any symbol not explicitly given in your legend will
|
||||||
fall back to its value in the default legend [outlined below](#default-legend).
|
fall back to its value in the default legend [outlined below](#default-legend).
|
||||||
|
|
||||||
- [MapNode](evennia.contrib.xyzgrid.xymap_legend.MapNode)
|
- [MapNode](evennia.contrib.grid.xyzgrid.xymap_legend.MapNode)
|
||||||
is the base class for all nodes.
|
is the base class for all nodes.
|
||||||
- [MapLink](evennia.contrib.xyzgrid.xymap_legend.MapLink)
|
- [MapLink](evennia.contrib.grid.xyzgrid.xymap_legend.MapLink)
|
||||||
is the base class for all links.
|
is the base class for all links.
|
||||||
|
|
||||||
As the _Map String_ is parsed, each found symbol is looked up in the legend and
|
As the _Map String_ is parsed, each found symbol is looked up in the legend and
|
||||||
|
|
@ -445,7 +445,7 @@ with a full set of map elements that use these properties in various ways
|
||||||
(described in the next section).
|
(described in the next section).
|
||||||
|
|
||||||
Some useful properties of the
|
Some useful properties of the
|
||||||
[MapNode](evennia.contrib.xyzgrid.xymap_legend.MapNode)
|
[MapNode](evennia.contrib.grid.xyzgrid.xymap_legend.MapNode)
|
||||||
class (see class doc for hook methods):
|
class (see class doc for hook methods):
|
||||||
|
|
||||||
- `symbol` (str) - The character to parse from the map into this node. By default this
|
- `symbol` (str) - The character to parse from the map into this node. By default this
|
||||||
|
|
@ -473,7 +473,7 @@ class (see class doc for hook methods):
|
||||||
useful for various reasons, mostly map-transitions).
|
useful for various reasons, mostly map-transitions).
|
||||||
|
|
||||||
Some useful properties of the
|
Some useful properties of the
|
||||||
[MapLink](evennia.contrib.xyzgrid.xymap_legend.MapLink)
|
[MapLink](evennia.contrib.grid.xyzgrid.xymap_legend.MapLink)
|
||||||
class (see class doc for hook methods):
|
class (see class doc for hook methods):
|
||||||
|
|
||||||
- `symbol` (str) - The character to parse from the map into this node. This must
|
- `symbol` (str) - The character to parse from the map into this node. This must
|
||||||
|
|
@ -530,7 +530,7 @@ Below is an example that changes the map's nodes to show up as red
|
||||||
(maybe for a lava map?):
|
(maybe for a lava map?):
|
||||||
|
|
||||||
```
|
```
|
||||||
from evennia.contrib.xyzgrid import xymap_legend
|
from evennia.contrib.grid.xyzgrid import xymap_legend
|
||||||
|
|
||||||
class RedMapNode(xymap_legend.MapNode):
|
class RedMapNode(xymap_legend.MapNode):
|
||||||
display_symbol = "|r#|n"
|
display_symbol = "|r#|n"
|
||||||
|
|
@ -548,7 +548,7 @@ LEGEND = {
|
||||||
Below is the default map legend. The `symbol` is what should be put in the Map
|
Below is the default map legend. The `symbol` is what should be put in the Map
|
||||||
string. It must always be a single character. The `display-symbol` is what is
|
string. It must always be a single character. The `display-symbol` is what is
|
||||||
actually visualized when displaying the map to players in-game. This could have
|
actually visualized when displaying the map to players in-game. This could have
|
||||||
colors etc. All classes are found in `evennia.contrib.xyzgrid.xymap_legend` and
|
colors etc. All classes are found in `evennia.contrib.grid.xyzgrid.xymap_legend` and
|
||||||
their names are included to make it easy to know what to override.
|
their names are included to make it easy to know what to override.
|
||||||
|
|
||||||
```{eval-rst}
|
```{eval-rst}
|
||||||
|
|
@ -801,7 +801,7 @@ different (unused) unique symbol in your map legend:
|
||||||
```python
|
```python
|
||||||
# in your map definition module
|
# in your map definition module
|
||||||
|
|
||||||
from evennia.contrib.xyzgrid import xymap_legend
|
from evennia.contrib.grid.xyzgrid import xymap_legend
|
||||||
|
|
||||||
MAPSTR = r"""
|
MAPSTR = r"""
|
||||||
|
|
||||||
|
|
@ -851,7 +851,7 @@ added, with different map-legend symbols:
|
||||||
```python
|
```python
|
||||||
# in your map definition module (let's say this is mapB)
|
# in your map definition module (let's say this is mapB)
|
||||||
|
|
||||||
from evennia.contrib.xyzgrid import xymap_legend
|
from evennia.contrib.grid.xyzgrid import xymap_legend
|
||||||
|
|
||||||
MAPSTR = r"""
|
MAPSTR = r"""
|
||||||
|
|
||||||
|
|
@ -924,10 +924,10 @@ across the map boundary.
|
||||||
[Prototypes](../Components/Prototypes.md) are dicts that describe how to _spawn_ a new instance
|
[Prototypes](../Components/Prototypes.md) are dicts that describe how to _spawn_ a new instance
|
||||||
of an object. Each of the _nodes_ and _links_ above have a default prototype
|
of an object. Each of the _nodes_ and _links_ above have a default prototype
|
||||||
that allows the `evennia xyzgrid spawn` command to convert them to
|
that allows the `evennia xyzgrid spawn` command to convert them to
|
||||||
a [XYZRoom](evennia.contrib.xyzgrid.xyzroom.XYZRoom)
|
a [XYZRoom](evennia.contrib.grid.xyzgrid.xyzroom.XYZRoom)
|
||||||
or an [XYZExit](evennia.contrib.xyzgrid.xyzroom.XYZRoom) respectively.
|
or an [XYZExit](evennia.contrib.grid.xyzgrid.xyzroom.XYZRoom) respectively.
|
||||||
|
|
||||||
The default prototypes are found in `evennia.contrib.xyzgrid.prototypes` (added
|
The default prototypes are found in `evennia.contrib.grid.xyzgrid.prototypes` (added
|
||||||
during installation of this contrib), with `prototype_key`s `"xyz_room"` and
|
during installation of this contrib), with `prototype_key`s `"xyz_room"` and
|
||||||
`"xyz_exit"` - use these as `prototype_parent` to add your own custom prototypes.
|
`"xyz_exit"` - use these as `prototype_parent` to add your own custom prototypes.
|
||||||
|
|
||||||
|
|
@ -1012,7 +1012,7 @@ picked up and applied to the existing objects.
|
||||||
|
|
||||||
#### Extending the base prototypes
|
#### Extending the base prototypes
|
||||||
|
|
||||||
The default prototypes are found in `evennia.contrib.xyzgrid.prototypes` and
|
The default prototypes are found in `evennia.contrib.grid.xyzgrid.prototypes` and
|
||||||
should be included as `prototype_parents` for prototypes on the map. Would it
|
should be included as `prototype_parents` for prototypes on the map. Would it
|
||||||
not be nice to be able to change these and have the change apply to all of the
|
not be nice to be able to change these and have the change apply to all of the
|
||||||
grid? You can, by adding the following to your `mygame/server/conf/settings.py`:
|
grid? You can, by adding the following to your `mygame/server/conf/settings.py`:
|
||||||
|
|
@ -1177,9 +1177,9 @@ To access the grid in-code, there are several ways:
|
||||||
grid = evennia.search_script("XYZGrid")[0]
|
grid = evennia.search_script("XYZGrid")[0]
|
||||||
|
|
||||||
(`search_script` always returns a list)
|
(`search_script` always returns a list)
|
||||||
- You can get it with `evennia.contrib.xyzgrid.xyzgrid.get_xyzgrid`
|
- You can get it with `evennia.contrib.grid.xyzgrid.xyzgrid.get_xyzgrid`
|
||||||
|
|
||||||
from evennia.contrib.xyzgrid.xyzgrid import get_xyzgrid
|
from evennia.contrib.grid.xyzgrid.xyzgrid import get_xyzgrid
|
||||||
grid = get_xyzgrid()
|
grid = get_xyzgrid()
|
||||||
|
|
||||||
This will *always* return a grid, creating an empty grid if one didn't
|
This will *always* return a grid, creating an empty grid if one didn't
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
```{eval-rst}
|
```{eval-rst}
|
||||||
evennia.contrib
|
evennia.contrib
|
||||||
=======================
|
=======================
|
||||||
|
|
||||||
.. automodule:: evennia.contrib
|
.. automodule:: evennia.contrib
|
||||||
|
|
@ -54,6 +54,6 @@ evennia.contrib
|
||||||
evennia.contrib.turnbattle
|
evennia.contrib.turnbattle
|
||||||
evennia.contrib.tutorial_examples
|
evennia.contrib.tutorial_examples
|
||||||
evennia.contrib.tutorial_world
|
evennia.contrib.tutorial_world
|
||||||
evennia.contrib.xyzgrid
|
evennia.contrib.grid.xyzgrid
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
```{eval-rst}
|
```{eval-rst}
|
||||||
evennia.contrib.xyzgrid.commands
|
evennia.contrib.grid.xyzgrid.commands
|
||||||
=======================================
|
=======================================
|
||||||
|
|
||||||
.. automodule:: evennia.contrib.xyzgrid.commands
|
.. automodule:: evennia.contrib.xyzgrid.commands
|
||||||
|
|
@ -7,4 +7,4 @@ evennia.contrib.xyzgrid.commands
|
||||||
:undoc-members:
|
:undoc-members:
|
||||||
:show-inheritance:
|
:show-inheritance:
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
|
||||||
|
|
@ -2162,7 +2162,7 @@ class TestCommsChannel(CommandTest):
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
from evennia.comms import comms # noqa
|
from evennia.commands.default import comms # noqa
|
||||||
|
|
||||||
|
|
||||||
class TestComms(CommandTest):
|
class TestComms(CommandTest):
|
||||||
|
|
@ -2179,13 +2179,13 @@ class TestComms(CommandTest):
|
||||||
|
|
||||||
class TestBatchProcess(CommandTest):
|
class TestBatchProcess(CommandTest):
|
||||||
|
|
||||||
@patch("evennia.contrib.tutorial_examples.red_button.repeat")
|
@patch("evennia.contrib.tutorials.red_button.red_button.repeat")
|
||||||
@patch("evennia.contrib.tutorial_examples.red_button.delay")
|
@patch("evennia.contrib.tutorials.red_button.red_button.delay")
|
||||||
def test_batch_commands(self, mock_delay, mock_repeat):
|
def test_batch_commands(self, mock_delay, mock_repeat):
|
||||||
# cannot test batchcode here, it must run inside the server process
|
# cannot test batchcode here, it must run inside the server process
|
||||||
self.call(
|
self.call(
|
||||||
batchprocess.CmdBatchCommands(),
|
batchprocess.CmdBatchCommands(),
|
||||||
"example_batch_cmds",
|
"batchprocessor.example_batch_cmds",
|
||||||
"Running Batch-command processor - Automatic mode for example_batch_cmds",
|
"Running Batch-command processor - Automatic mode for example_batch_cmds",
|
||||||
)
|
)
|
||||||
# we make sure to delete the button again here to stop the running reactor
|
# we make sure to delete the button again here to stop the running reactor
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
"""
|
"""
|
||||||
Extened Room - Griatch 2012, vincent-lg 2019
|
Contribs related to moving in and manipulating the game world and grid.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -21,7 +21,7 @@ class ForceUTCDatetime(datetime.datetime):
|
||||||
return datetime.datetime.utcfromtimestamp(timestamp)
|
return datetime.datetime.utcfromtimestamp(timestamp)
|
||||||
|
|
||||||
|
|
||||||
@patch("evennia.contrib.extended_room.datetime.datetime", ForceUTCDatetime)
|
@patch("evennia.contrib.grid.extended_room.extended_room.datetime.datetime", ForceUTCDatetime)
|
||||||
# mock gametime to return April 9, 2064, at 21:06 (spring evening)
|
# mock gametime to return April 9, 2064, at 21:06 (spring evening)
|
||||||
@patch("evennia.utils.gametime.gametime", new=Mock(return_value=2975000766))
|
@patch("evennia.utils.gametime.gametime", new=Mock(return_value=2975000766))
|
||||||
class TestExtendedRoom(CommandTest):
|
class TestExtendedRoom(CommandTest):
|
||||||
|
|
|
||||||
|
|
@ -6,12 +6,193 @@ Test map builder.
|
||||||
from evennia.commands.default.tests import CommandTest
|
from evennia.commands.default.tests import CommandTest
|
||||||
from . import mapbuilder
|
from . import mapbuilder
|
||||||
|
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
# Add the necessary imports for your instructions here.
|
||||||
|
from evennia import create_object
|
||||||
|
from typeclasses import rooms, exits
|
||||||
|
from random import randint
|
||||||
|
import random
|
||||||
|
|
||||||
|
|
||||||
|
# A map with a temple (▲) amongst mountains (n,∩) in a forest (♣,♠) on an
|
||||||
|
# island surrounded by water (≈). By giving no instructions for the water
|
||||||
|
# characters we effectively skip it and create no rooms for those squares.
|
||||||
|
EXAMPLE1_MAP = '''
|
||||||
|
≈≈≈≈≈
|
||||||
|
≈♣n♣≈
|
||||||
|
≈∩▲∩≈
|
||||||
|
≈♠n♠≈
|
||||||
|
≈≈≈≈≈
|
||||||
|
'''
|
||||||
|
|
||||||
|
def example1_build_forest(x, y, **kwargs):
|
||||||
|
'''A basic example of build instructions. Make sure to include **kwargs
|
||||||
|
in the arguments and return an instance of the room for exit generation.'''
|
||||||
|
|
||||||
|
# Create a room and provide a basic description.
|
||||||
|
room = create_object(rooms.Room, key="forest" + str(x) + str(y))
|
||||||
|
room.db.desc = "Basic forest room."
|
||||||
|
|
||||||
|
# Send a message to the account
|
||||||
|
kwargs["caller"].msg(room.key + " " + room.dbref)
|
||||||
|
|
||||||
|
# This is generally mandatory.
|
||||||
|
return room
|
||||||
|
|
||||||
|
|
||||||
|
def example1_build_mountains(x, y, **kwargs):
|
||||||
|
'''A room that is a little more advanced'''
|
||||||
|
|
||||||
|
# Create the room.
|
||||||
|
room = create_object(rooms.Room, key="mountains" + str(x) + str(y))
|
||||||
|
|
||||||
|
# Generate a description by randomly selecting an entry from a list.
|
||||||
|
room_desc = [
|
||||||
|
"Mountains as far as the eye can see",
|
||||||
|
"Your path is surrounded by sheer cliffs",
|
||||||
|
"Haven't you seen that rock before?",
|
||||||
|
]
|
||||||
|
room.db.desc = random.choice(room_desc)
|
||||||
|
|
||||||
|
# Create a random number of objects to populate the room.
|
||||||
|
for i in range(randint(0, 3)):
|
||||||
|
rock = create_object(key="Rock", location=room)
|
||||||
|
rock.db.desc = "An ordinary rock."
|
||||||
|
|
||||||
|
# Send a message to the account
|
||||||
|
kwargs["caller"].msg(room.key + " " + room.dbref)
|
||||||
|
|
||||||
|
# This is generally mandatory.
|
||||||
|
return room
|
||||||
|
|
||||||
|
|
||||||
|
def example1_build_temple(x, y, **kwargs):
|
||||||
|
'''A unique room that does not need to be as general'''
|
||||||
|
|
||||||
|
# Create the room.
|
||||||
|
room = create_object(rooms.Room, key="temple" + str(x) + str(y))
|
||||||
|
|
||||||
|
# Set the description.
|
||||||
|
room.db.desc = (
|
||||||
|
"In what, from the outside, appeared to be a grand and "
|
||||||
|
"ancient temple you've somehow found yourself in the the "
|
||||||
|
"Evennia Inn! It consists of one large room filled with "
|
||||||
|
"tables. The bardisk extends along the east wall, where "
|
||||||
|
"multiple barrels and bottles line the shelves. The "
|
||||||
|
"barkeep seems busy handing out ale and chatting with "
|
||||||
|
"the patrons, which are a rowdy and cheerful lot, "
|
||||||
|
"keeping the sound level only just below thunderous. "
|
||||||
|
"This is a rare spot of mirth on this dread moor."
|
||||||
|
)
|
||||||
|
|
||||||
|
# Send a message to the account
|
||||||
|
kwargs["caller"].msg(room.key + " " + room.dbref)
|
||||||
|
|
||||||
|
# This is generally mandatory.
|
||||||
|
return room
|
||||||
|
|
||||||
|
|
||||||
|
# Include your trigger characters and build functions in a legend dict.
|
||||||
|
EXAMPLE1_LEGEND = {
|
||||||
|
("♣", "♠"): example1_build_forest,
|
||||||
|
("∩", "n"): example1_build_mountains,
|
||||||
|
("▲"): example1_build_temple,
|
||||||
|
}
|
||||||
|
|
||||||
|
# Example two
|
||||||
|
|
||||||
|
# @mapbuilder/two evennia.contrib.mapbuilder.EXAMPLE2_MAP EXAMPLE2_LEGEND
|
||||||
|
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
# Add the necessary imports for your instructions here.
|
||||||
|
# from evennia import create_object
|
||||||
|
# from typeclasses import rooms, exits
|
||||||
|
# from evennia.utils import utils
|
||||||
|
# from random import randint
|
||||||
|
# import random
|
||||||
|
|
||||||
|
# This is the same layout as Example 1 but included are characters for exits.
|
||||||
|
# We can use these characters to determine which rooms should be connected.
|
||||||
|
EXAMPLE2_MAP = '''
|
||||||
|
≈ ≈ ≈ ≈ ≈
|
||||||
|
|
||||||
|
≈ ♣-♣-♣ ≈
|
||||||
|
| |
|
||||||
|
≈ ♣ ♣ ♣ ≈
|
||||||
|
| | |
|
||||||
|
≈ ♣-♣-♣ ≈
|
||||||
|
|
||||||
|
≈ ≈ ≈ ≈ ≈
|
||||||
|
'''
|
||||||
|
|
||||||
|
|
||||||
|
def example2_build_forest(x, y, **kwargs):
|
||||||
|
'''A basic room'''
|
||||||
|
# If on anything other than the first iteration - Do nothing.
|
||||||
|
if kwargs["iteration"] > 0:
|
||||||
|
return None
|
||||||
|
|
||||||
|
room = create_object(rooms.Room, key="forest" + str(x) + str(y))
|
||||||
|
room.db.desc = "Basic forest room."
|
||||||
|
|
||||||
|
kwargs["caller"].msg(room.key + " " + room.dbref)
|
||||||
|
|
||||||
|
return room
|
||||||
|
|
||||||
|
|
||||||
|
def example2_build_verticle_exit(x, y, **kwargs):
|
||||||
|
'''Creates two exits to and from the two rooms north and south.'''
|
||||||
|
# If on the first iteration - Do nothing.
|
||||||
|
if kwargs["iteration"] == 0:
|
||||||
|
return
|
||||||
|
|
||||||
|
north_room = kwargs["room_dict"][(x, y - 1)]
|
||||||
|
south_room = kwargs["room_dict"][(x, y + 1)]
|
||||||
|
|
||||||
|
# create exits in the rooms
|
||||||
|
create_object(
|
||||||
|
exits.Exit, key="south", aliases=["s"], location=north_room, destination=south_room
|
||||||
|
)
|
||||||
|
|
||||||
|
create_object(
|
||||||
|
exits.Exit, key="north", aliases=["n"], location=south_room, destination=north_room
|
||||||
|
)
|
||||||
|
|
||||||
|
kwargs["caller"].msg("Connected: " + north_room.key + " & " + south_room.key)
|
||||||
|
|
||||||
|
|
||||||
|
def example2_build_horizontal_exit(x, y, **kwargs):
|
||||||
|
'''Creates two exits to and from the two rooms east and west.'''
|
||||||
|
# If on the first iteration - Do nothing.
|
||||||
|
if kwargs["iteration"] == 0:
|
||||||
|
return
|
||||||
|
|
||||||
|
west_room = kwargs["room_dict"][(x - 1, y)]
|
||||||
|
east_room = kwargs["room_dict"][(x + 1, y)]
|
||||||
|
|
||||||
|
create_object(exits.Exit, key="east", aliases=["e"], location=west_room, destination=east_room)
|
||||||
|
|
||||||
|
create_object(exits.Exit, key="west", aliases=["w"], location=east_room, destination=west_room)
|
||||||
|
|
||||||
|
kwargs["caller"].msg("Connected: " + west_room.key + " & " + east_room.key)
|
||||||
|
|
||||||
|
|
||||||
|
# Include your trigger characters and build functions in a legend dict.
|
||||||
|
EXAMPLE2_LEGEND = {
|
||||||
|
("♣", "♠"): example2_build_forest,
|
||||||
|
("|"): example2_build_verticle_exit,
|
||||||
|
("-"): example2_build_horizontal_exit,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
class TestMapBuilder(CommandTest):
|
class TestMapBuilder(CommandTest):
|
||||||
def test_cmdmapbuilder(self):
|
def test_cmdmapbuilder(self):
|
||||||
self.call(
|
self.call(
|
||||||
mapbuilder.CmdMapBuilder(),
|
mapbuilder.CmdMapBuilder(),
|
||||||
"evennia.contrib.mapbuilder.EXAMPLE1_MAP evennia.contrib.mapbuilder.EXAMPLE1_LEGEND",
|
"evennia.contrib.grid.mapbuilder.tests.EXAMPLE1_MAP "
|
||||||
|
"evennia.contrib.grid.mapbuilder.tests.EXAMPLE1_LEGEND",
|
||||||
"""Creating Map...|≈≈≈≈≈
|
"""Creating Map...|≈≈≈≈≈
|
||||||
≈♣n♣≈
|
≈♣n♣≈
|
||||||
≈∩▲∩≈
|
≈∩▲∩≈
|
||||||
|
|
@ -21,7 +202,8 @@ class TestMapBuilder(CommandTest):
|
||||||
)
|
)
|
||||||
self.call(
|
self.call(
|
||||||
mapbuilder.CmdMapBuilder(),
|
mapbuilder.CmdMapBuilder(),
|
||||||
"evennia.contrib.mapbuilder.EXAMPLE2_MAP evennia.contrib.mapbuilder.EXAMPLE2_LEGEND",
|
"evennia.contrib.grid.mapbuilder.tests.EXAMPLE2_MAP "
|
||||||
|
"evennia.contrib.grid.mapbuilder.tests.EXAMPLE2_LEGEND",
|
||||||
"""Creating Map...|≈ ≈ ≈ ≈ ≈
|
"""Creating Map...|≈ ≈ ≈ ≈ ≈
|
||||||
|
|
||||||
≈ ♣-♣-♣ ≈
|
≈ ♣-♣-♣ ≈
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,7 @@ class TestSimpleDoor(CommandTest):
|
||||||
def test_cmdopen(self):
|
def test_cmdopen(self):
|
||||||
self.call(
|
self.call(
|
||||||
simpledoor.CmdOpen(),
|
simpledoor.CmdOpen(),
|
||||||
"newdoor;door:contrib.simpledoor.SimpleDoor,backdoor;door = Room2",
|
"newdoor;door:contrib.grid.simpledoor.SimpleDoor,backdoor;door = Room2",
|
||||||
"Created new Exit 'newdoor' from Room to Room2 (aliases: door).|Note: A door-type exit was "
|
"Created new Exit 'newdoor' from Room to Room2 (aliases: door).|Note: A door-type exit was "
|
||||||
"created - ignored eventual custom return-exit type.|Created new Exit 'newdoor' from Room2 to Room (aliases: door).",
|
"created - ignored eventual custom return-exit type.|Created new Exit 'newdoor' from Room2 to Room (aliases: door).",
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
"""
|
"""
|
||||||
XYZGrid - Evennia 2021
|
XYZGrid - Griatch 2021
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -4,12 +4,12 @@ the `evennia xyzgrid` launcher command.
|
||||||
|
|
||||||
First add the launcher extension in your mygame/server/conf/settings.py:
|
First add the launcher extension in your mygame/server/conf/settings.py:
|
||||||
|
|
||||||
EXTRA_LAUNCHER_COMMANDS['xyzgrid'] = 'evennia.contrib.xyzgrid.launchcmd.xyzcommand'
|
EXTRA_LAUNCHER_COMMANDS['xyzgrid'] = 'evennia.contrib.grid.xyzgrid.launchcmd.xyzcommand'
|
||||||
|
|
||||||
Then
|
Then
|
||||||
|
|
||||||
evennia xyzgrid init
|
evennia xyzgrid init
|
||||||
evennia xyzgrid add evennia.contrib.xyzgrid.map_example
|
evennia xyzgrid add evennia.contrib.grid.xyzgrid.map_example
|
||||||
evennia xyzgrid build
|
evennia xyzgrid build
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -21,7 +21,7 @@ from evennia.contrib.grid.xyzgrid import xymap_legend
|
||||||
|
|
||||||
# default prototype parent. It's important that
|
# default prototype parent. It's important that
|
||||||
# the typeclass inherits from the XYZRoom (or XYZExit)
|
# the typeclass inherits from the XYZRoom (or XYZExit)
|
||||||
# if adding the evennia.contrib.xyzgrid.prototypes to
|
# if adding the evennia.contrib.grid.xyzgrid.prototypes to
|
||||||
# settings.PROTOTYPE_MODULES, one could just set the
|
# settings.PROTOTYPE_MODULES, one could just set the
|
||||||
# prototype_parent to 'xyz_room' and 'xyz_exit' here
|
# prototype_parent to 'xyz_room' and 'xyz_exit' here
|
||||||
# instead.
|
# instead.
|
||||||
|
|
@ -30,14 +30,14 @@ ROOM_PARENT = {
|
||||||
"key": "An empty room",
|
"key": "An empty room",
|
||||||
"prototype_key": "xyz_exit_prototype",
|
"prototype_key": "xyz_exit_prototype",
|
||||||
# "prototype_parent": "xyz_room",
|
# "prototype_parent": "xyz_room",
|
||||||
"typeclass": "evennia.contrib.xyzgrid.xyzroom.XYZRoom",
|
"typeclass": "evennia.contrib.grid.xyzgrid.xyzroom.XYZRoom",
|
||||||
"desc": "An empty room.",
|
"desc": "An empty room.",
|
||||||
}
|
}
|
||||||
|
|
||||||
EXIT_PARENT = {
|
EXIT_PARENT = {
|
||||||
"prototype_key": "xyz_exit_prototype",
|
"prototype_key": "xyz_exit_prototype",
|
||||||
# "prototype_parent": "xyz_exit",
|
# "prototype_parent": "xyz_exit",
|
||||||
"typeclass": "evennia.contrib.xyzgrid.xyzroom.XYZExit",
|
"typeclass": "evennia.contrib.grid.xyzgrid.xyzroom.XYZExit",
|
||||||
"desc": "A path to the next location.",
|
"desc": "A path to the next location.",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@ server (since this can be slow).
|
||||||
To use, add to the settings:
|
To use, add to the settings:
|
||||||
::
|
::
|
||||||
|
|
||||||
EXTRA_LAUNCHER_COMMANDS.update({'xyzgrid': 'evennia.contrib.xyzgrid.launchcmd.xyzcommand'})
|
EXTRA_LAUNCHER_COMMANDS.update({'xyzgrid': 'evennia.contrib.grid.xyzgrid.launchcmd.xyzcommand'})
|
||||||
|
|
||||||
You should now be able to do
|
You should now be able to do
|
||||||
::
|
::
|
||||||
|
|
@ -80,7 +80,7 @@ add <path.to.xymap.module> [<path> <path>,...]
|
||||||
{"map": mapstring, "zcoord": mapname/zcoord, "legend": dict, "prototypes": dict}
|
{"map": mapstring, "zcoord": mapname/zcoord, "legend": dict, "prototypes": dict}
|
||||||
describing one single XYmap, or
|
describing one single XYmap, or
|
||||||
- a XYMAP_DATA_LIST - a list of multiple dicts on the XYMAP_DATA form. This allows for
|
- a XYMAP_DATA_LIST - a list of multiple dicts on the XYMAP_DATA form. This allows for
|
||||||
embedding multiple maps in the same module. See evennia/contrib/xyzgrid/example.py
|
embedding multiple maps in the same module. See evennia/contrib/grid/xyzgrid/example.py
|
||||||
for an example of how this looks.
|
for an example of how this looks.
|
||||||
|
|
||||||
Note that adding a map does *not* spawn it. If maps are linked to one another, you should
|
Note that adding a map does *not* spawn it. If maps are linked to one another, you should
|
||||||
|
|
@ -89,7 +89,7 @@ add <path.to.xymap.module> [<path> <path>,...]
|
||||||
|
|
||||||
Examples:
|
Examples:
|
||||||
|
|
||||||
evennia xyzgrid add evennia.contrib.xyzgrid.example
|
evennia xyzgrid add evennia.contrib.grid.xyzgrid.example
|
||||||
evennia xyzgrid add world.mymap1 world.mymap2 world.mymap3
|
evennia xyzgrid add world.mymap1 world.mymap2 world.mymap3
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@ Default prototypes for building the XYZ-grid into actual game-rooms.
|
||||||
|
|
||||||
Add this to mygame/conf/settings/settings.py:
|
Add this to mygame/conf/settings/settings.py:
|
||||||
|
|
||||||
PROTOTYPE_MODULES += ['evennia.contrib.xyzgrid.prototypes']
|
PROTOTYPE_MODULES += ['evennia.contrib.grid.xyzgrid.prototypes']
|
||||||
|
|
||||||
The prototypes can then be used in mapping prototypes as
|
The prototypes can then be used in mapping prototypes as
|
||||||
|
|
||||||
|
|
@ -28,7 +28,7 @@ except AttributeError:
|
||||||
|
|
||||||
room_prototype = {
|
room_prototype = {
|
||||||
'prototype_key': 'xyz_room',
|
'prototype_key': 'xyz_room',
|
||||||
'typeclass': 'evennia.contrib.xyzgrid.xyzroom.XYZRoom',
|
'typeclass': 'evennia.contrib.grid.xyzgrid.xyzroom.XYZRoom',
|
||||||
'prototype_tags': ("xyzroom", ),
|
'prototype_tags': ("xyzroom", ),
|
||||||
'key': "A room",
|
'key': "A room",
|
||||||
'desc': "An empty room."
|
'desc': "An empty room."
|
||||||
|
|
@ -37,7 +37,7 @@ room_prototype.update(room_override)
|
||||||
|
|
||||||
exit_prototype = {
|
exit_prototype = {
|
||||||
'prototype_key': 'xyz_exit',
|
'prototype_key': 'xyz_exit',
|
||||||
'typeclass': 'evennia.contrib.xyzgrid.xyzroom.XYZExit',
|
'typeclass': 'evennia.contrib.grid.xyzgrid.xyzroom.XYZExit',
|
||||||
'prototype_tags': ("xyzexit", ),
|
'prototype_tags': ("xyzexit", ),
|
||||||
'desc': "An exit."
|
'desc': "An exit."
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -854,8 +854,6 @@ class TestMap8(_MapTest):
|
||||||
target=target, target_path_style="",
|
target=target, target_path_style="",
|
||||||
character='@',
|
character='@',
|
||||||
max_size=max_size)
|
max_size=max_size)
|
||||||
self.assertEqual(expected, mapstr.replace("||", "|"))
|
|
||||||
|
|
||||||
def test_spawn(self):
|
def test_spawn(self):
|
||||||
"""
|
"""
|
||||||
Spawn the map into actual objects.
|
Spawn the map into actual objects.
|
||||||
|
|
@ -1018,6 +1016,8 @@ class TestMap11(_MapTest):
|
||||||
target=target, target_path_style="",
|
target=target, target_path_style="",
|
||||||
character='@',
|
character='@',
|
||||||
max_size=max_size)
|
max_size=max_size)
|
||||||
|
print(f"\n\n{coord}-{target}\n{expected}\n\n{mapstr}")
|
||||||
|
|
||||||
self.assertEqual(expected, mapstr)
|
self.assertEqual(expected, mapstr)
|
||||||
|
|
||||||
def test_spawn(self):
|
def test_spawn(self):
|
||||||
|
|
@ -1252,6 +1252,7 @@ class TestXYZGridTransition(EvenniaTest):
|
||||||
self.assertEqual(east_exit.db_destination, room2)
|
self.assertEqual(east_exit.db_destination, room2)
|
||||||
self.assertEqual(west_exit.db_destination, room1)
|
self.assertEqual(west_exit.db_destination, room1)
|
||||||
|
|
||||||
|
|
||||||
class TestBuildExampleGrid(EvenniaTest):
|
class TestBuildExampleGrid(EvenniaTest):
|
||||||
"""
|
"""
|
||||||
Test building the map-example (this takes about 30s)
|
Test building the map-example (this takes about 30s)
|
||||||
|
|
@ -1274,7 +1275,7 @@ class TestBuildExampleGrid(EvenniaTest):
|
||||||
Build the map example.
|
Build the map example.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
mapdatas = self.grid.maps_from_module("evennia.contrib.xyzgrid.example")
|
mapdatas = self.grid.maps_from_module("evennia.contrib.grid.xyzgrid.example")
|
||||||
self.assertEqual(len(mapdatas), 2)
|
self.assertEqual(len(mapdatas), 2)
|
||||||
|
|
||||||
self.grid.add_maps(*mapdatas)
|
self.grid.add_maps(*mapdatas)
|
||||||
|
|
|
||||||
|
|
@ -210,7 +210,7 @@ class XYMap:
|
||||||
if not _LOADED_PROTOTYPES:
|
if not _LOADED_PROTOTYPES:
|
||||||
# inject default prototypes, but don't override prototype-keys loaded from
|
# inject default prototypes, but don't override prototype-keys loaded from
|
||||||
# settings, if they exist (that means the user wants to replace the defaults)
|
# settings, if they exist (that means the user wants to replace the defaults)
|
||||||
protlib.load_module_prototypes("evennia.contrib.xyzgrid.prototypes", override=False)
|
protlib.load_module_prototypes("evennia.contrib.grid.xyzgrid.prototypes", override=False)
|
||||||
_LOADED_PROTOTYPES = True
|
_LOADED_PROTOTYPES = True
|
||||||
|
|
||||||
self.Z = Z
|
self.Z = Z
|
||||||
|
|
@ -636,7 +636,7 @@ class XYMap:
|
||||||
"""
|
"""
|
||||||
global _XYZROOMCLASS
|
global _XYZROOMCLASS
|
||||||
if not _XYZROOMCLASS:
|
if not _XYZROOMCLASS:
|
||||||
from evennia.contrib.xyzgrid.xyzroom import XYZRoom as _XYZROOMCLASS
|
from evennia.contrib.grid.xyzgrid.xyzroom import XYZRoom as _XYZROOMCLASS
|
||||||
x, y = xy
|
x, y = xy
|
||||||
wildcard = '*'
|
wildcard = '*'
|
||||||
spawned = []
|
spawned = []
|
||||||
|
|
|
||||||
|
|
@ -315,7 +315,7 @@ class MapNode:
|
||||||
# create a new entity with proper coordinates etc
|
# create a new entity with proper coordinates etc
|
||||||
tclass = self.prototype['typeclass']
|
tclass = self.prototype['typeclass']
|
||||||
tclass = (f' ({tclass})'
|
tclass = (f' ({tclass})'
|
||||||
if tclass != 'evennia.contrib.xyzgrid.xyzroom.XYZRoom'
|
if tclass != 'evennia.contrib.grid.xyzgrid.xyzroom.XYZRoom'
|
||||||
else '')
|
else '')
|
||||||
self.log(f" spawning room at xyz={xyz}{tclass}")
|
self.log(f" spawning room at xyz={xyz}{tclass}")
|
||||||
nodeobj, err = NodeTypeclass.create(
|
nodeobj, err = NodeTypeclass.create(
|
||||||
|
|
@ -413,7 +413,7 @@ class MapNode:
|
||||||
prot = maplinks[key.lower()][3].prototype
|
prot = maplinks[key.lower()][3].prototype
|
||||||
tclass = prot['typeclass']
|
tclass = prot['typeclass']
|
||||||
tclass = (f' ({tclass})'
|
tclass = (f' ({tclass})'
|
||||||
if tclass != 'evennia.contrib.xyzgrid.xyzroom.XYZExit'
|
if tclass != 'evennia.contrib.grid.xyzgrid.xyzroom.XYZExit'
|
||||||
else '')
|
else '')
|
||||||
self.log(f" spawning/updating exit xyz={xyz}, direction={key}{tclass}")
|
self.log(f" spawning/updating exit xyz={xyz}, direction={key}{tclass}")
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -278,7 +278,7 @@ class XYZRoom(DefaultRoom):
|
||||||
def xyzgrid(self):
|
def xyzgrid(self):
|
||||||
global GET_XYZGRID
|
global GET_XYZGRID
|
||||||
if not GET_XYZGRID:
|
if not GET_XYZGRID:
|
||||||
from evennia.contrib.xyzgrid.xyzgrid import get_xyzgrid as GET_XYZGRID
|
from evennia.contrib.grid.xyzgrid.xyzgrid import get_xyzgrid as GET_XYZGRID
|
||||||
return GET_XYZGRID()
|
return GET_XYZGRID()
|
||||||
|
|
||||||
@property
|
@property
|
||||||
|
|
@ -493,7 +493,7 @@ class XYZExit(DefaultExit):
|
||||||
def xyzgrid(self):
|
def xyzgrid(self):
|
||||||
global GET_XYZGRID
|
global GET_XYZGRID
|
||||||
if not GET_XYZGRID:
|
if not GET_XYZGRID:
|
||||||
from evennia.contrib.xyzgrid.xyzgrid import get_xyzgrid as GET_XYZGRID
|
from evennia.contrib.grid.xyzgrid.xyzgrid import get_xyzgrid as GET_XYZGRID
|
||||||
return GET_XYZGRID()
|
return GET_XYZGRID()
|
||||||
|
|
||||||
@property
|
@property
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@
|
||||||
# It allows batch processing of normal Evennia commands.
|
# It allows batch processing of normal Evennia commands.
|
||||||
# Test it by loading it with @batchcommand:
|
# Test it by loading it with @batchcommand:
|
||||||
#
|
#
|
||||||
# @batchcommand[/interactive] examples.batch_example
|
# batchcommand[/interactive] examples.batch_example
|
||||||
#
|
#
|
||||||
# A # as the first symbol on a line begins a comment and
|
# A # as the first symbol on a line begins a comment and
|
||||||
# marks the end of a previous command definition (important!).
|
# marks the end of a previous command definition (important!).
|
||||||
|
|
@ -17,12 +17,12 @@
|
||||||
|
|
||||||
# This creates a red button
|
# This creates a red button
|
||||||
|
|
||||||
@create button:tutorial_examples.red_button.RedButton
|
create button:red_button.RedButton
|
||||||
|
|
||||||
# This comment ends input for @create
|
# This comment ends input for @create
|
||||||
# Next command:
|
# Next command:
|
||||||
|
|
||||||
@set button/desc =
|
set button/desc =
|
||||||
This is a large red button. Now and then
|
This is a large red button. Now and then
|
||||||
it flashes in an evil, yet strangely tantalizing way.
|
it flashes in an evil, yet strangely tantalizing way.
|
||||||
|
|
||||||
|
|
@ -48,7 +48,7 @@ know you want to!
|
||||||
# Now let's place the button where it belongs (let's say limbo #2 is
|
# Now let's place the button where it belongs (let's say limbo #2 is
|
||||||
# the evil lair in our example).
|
# the evil lair in our example).
|
||||||
|
|
||||||
@teleport #2
|
teleport #2
|
||||||
|
|
||||||
#... and drop it (remember, this comment ends input to @teleport, so don't
|
#... and drop it (remember, this comment ends input to @teleport, so don't
|
||||||
#forget it!) The very last command in the file need not be ended with #.
|
#forget it!) The very last command in the file need not be ended with #.
|
||||||
|
|
|
||||||
|
|
@ -45,7 +45,7 @@
|
||||||
# all other #CODE blocks when they are executed.
|
# all other #CODE blocks when they are executed.
|
||||||
|
|
||||||
from evennia import create_object, search_object
|
from evennia import create_object, search_object
|
||||||
from evennia.contrib.tutorial_examples import red_button
|
from evennia.contrib.tutorials import red_button
|
||||||
from evennia import DefaultObject
|
from evennia import DefaultObject
|
||||||
|
|
||||||
limbo = search_object("Limbo")[0]
|
limbo = search_object("Limbo")[0]
|
||||||
|
|
|
||||||
|
|
@ -32,14 +32,14 @@ Files included in this module:
|
||||||
Deployment is completed by configuring a few settings in server.conf. This line
|
Deployment is completed by configuring a few settings in server.conf. This line
|
||||||
is required:
|
is required:
|
||||||
|
|
||||||
SERVER_SESSION_CLASS = 'evennia.contrib.security.auditing.server.AuditedServerSession'
|
SERVER_SESSION_CLASS = 'evennia.contrib.utils.auditing.server.AuditedServerSession'
|
||||||
|
|
||||||
This tells Evennia to use this ServerSession instead of its own. Below are the
|
This tells Evennia to use this ServerSession instead of its own. Below are the
|
||||||
other possible options along with the default value that will be used if unset.
|
other possible options along with the default value that will be used if unset.
|
||||||
|
|
||||||
# Where to send logs? Define the path to a module containing your callback
|
# Where to send logs? Define the path to a module containing your callback
|
||||||
# function. It should take a single dict argument as input
|
# function. It should take a single dict argument as input
|
||||||
AUDIT_CALLBACK = 'evennia.contrib.security.auditing.outputs.to_file'
|
AUDIT_CALLBACK = 'evennia.contrib.utils.auditing.outputs.to_file'
|
||||||
|
|
||||||
# Log user input? Be ethical about this; it will log all private and
|
# Log user input? Be ethical about this; it will log all private and
|
||||||
# public communications between players and/or admins (default: False).
|
# public communications between players and/or admins (default: False).
|
||||||
|
|
|
||||||
|
|
@ -16,7 +16,7 @@ from evennia.server.serversession import ServerSession
|
||||||
|
|
||||||
# Attributes governing auditing of commands and where to send log objects
|
# Attributes governing auditing of commands and where to send log objects
|
||||||
AUDIT_CALLBACK = getattr(
|
AUDIT_CALLBACK = getattr(
|
||||||
ev_settings, "AUDIT_CALLBACK", "evennia.contrib.security.auditing.outputs.to_file"
|
ev_settings, "AUDIT_CALLBACK", "evennia.contrib.utils.auditing.outputs.to_file"
|
||||||
)
|
)
|
||||||
AUDIT_IN = getattr(ev_settings, "AUDIT_IN", False)
|
AUDIT_IN = getattr(ev_settings, "AUDIT_IN", False)
|
||||||
AUDIT_OUT = getattr(ev_settings, "AUDIT_OUT", False)
|
AUDIT_OUT = getattr(ev_settings, "AUDIT_OUT", False)
|
||||||
|
|
|
||||||
|
|
@ -8,13 +8,13 @@ from evennia.utils.test_resources import EvenniaTest
|
||||||
import re
|
import re
|
||||||
|
|
||||||
# Configure session auditing settings - TODO: This is bad practice that leaks over to other tests
|
# 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.contrib.utils.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 - TODO: This is bad practice, changing global settings
|
# 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.utils.auditing.server.AuditedServerSession"
|
||||||
|
|
||||||
|
|
||||||
class AuditingTest(EvenniaTest):
|
class AuditingTest(EvenniaTest):
|
||||||
|
|
|
||||||
|
|
@ -399,7 +399,7 @@ class ServerSessionHandler(SessionHandler):
|
||||||
self.server.at_post_portal_sync(mode)
|
self.server.at_post_portal_sync(mode)
|
||||||
# announce the reconnection
|
# announce the reconnection
|
||||||
if _BROADCAST_SERVER_RESTART_MESSAGES:
|
if _BROADCAST_SERVER_RESTART_MESSAGES:
|
||||||
self.announce_all(_(" ... Server restarted."))
|
self.announce_all(_(" ... Server restarted."))
|
||||||
|
|
||||||
def portal_disconnect(self, session):
|
def portal_disconnect(self, session):
|
||||||
"""
|
"""
|
||||||
|
|
|
||||||
|
|
@ -508,7 +508,7 @@ TYPECLASS_PATHS = [
|
||||||
"typeclasses",
|
"typeclasses",
|
||||||
"evennia",
|
"evennia",
|
||||||
"evennia.contrib",
|
"evennia.contrib",
|
||||||
"evennia.contrib.tutorial_examples",
|
"evennia.contrib.tutorials",
|
||||||
]
|
]
|
||||||
|
|
||||||
# Typeclass for account objects (linked to a character) (fallback)
|
# Typeclass for account objects (linked to a character) (fallback)
|
||||||
|
|
@ -602,7 +602,7 @@ VALIDATOR_FUNC_MODULES = ["evennia.utils.validatorfuncs"]
|
||||||
BASE_BATCHPROCESS_PATHS = [
|
BASE_BATCHPROCESS_PATHS = [
|
||||||
"world",
|
"world",
|
||||||
"evennia.contrib",
|
"evennia.contrib",
|
||||||
"evennia.contrib.tutorial_examples",
|
"evennia.contrib.tutorials",
|
||||||
]
|
]
|
||||||
|
|
||||||
######################################################################
|
######################################################################
|
||||||
|
|
|
||||||
|
|
@ -1,5 +0,0 @@
|
||||||
# Security
|
|
||||||
|
|
||||||
This directory contains security-related contribs
|
|
||||||
|
|
||||||
- Auditing (Johnny 2018) - Allow for optional security logging of user input/output.
|
|
||||||
|
|
@ -1,72 +0,0 @@
|
||||||
# Input/Output Auditing
|
|
||||||
|
|
||||||
Contrib - Johnny 2017
|
|
||||||
|
|
||||||
This is a tap that optionally intercepts all data sent to/from clients and the
|
|
||||||
server and passes it to a callback of your choosing.
|
|
||||||
|
|
||||||
It is intended for quality assurance, post-incident investigations and debugging
|
|
||||||
but obviously can be abused. All data is recorded in cleartext. Please
|
|
||||||
be ethical, and if you are unwilling to properly deal with the implications of
|
|
||||||
recording user passwords or private communications, please do not enable
|
|
||||||
this module.
|
|
||||||
|
|
||||||
Some checks have been implemented to protect the privacy of users.
|
|
||||||
|
|
||||||
|
|
||||||
Files included in this module:
|
|
||||||
|
|
||||||
outputs.py - Example callback methods. This module ships with examples of
|
|
||||||
callbacks that send data as JSON to a file in your game/server/logs
|
|
||||||
dir or to your native Linux syslog daemon. You can of course write
|
|
||||||
your own to do other things like post them to Kafka topics.
|
|
||||||
|
|
||||||
server.py - Extends the Evennia ServerSession object to pipe data to the
|
|
||||||
callback upon receipt.
|
|
||||||
|
|
||||||
tests.py - Unit tests that check to make sure commands with sensitive
|
|
||||||
arguments are having their PII scrubbed.
|
|
||||||
|
|
||||||
|
|
||||||
Installation/Configuration:
|
|
||||||
|
|
||||||
Deployment is completed by configuring a few settings in server.conf. This line
|
|
||||||
is required:
|
|
||||||
|
|
||||||
SERVER_SESSION_CLASS = 'evennia.contrib.security.auditing.server.AuditedServerSession'
|
|
||||||
|
|
||||||
This tells Evennia to use this ServerSession instead of its own. Below are the
|
|
||||||
other possible options along with the default value that will be used if unset.
|
|
||||||
|
|
||||||
# Where to send logs? Define the path to a module containing your callback
|
|
||||||
# function. It should take a single dict argument as input
|
|
||||||
AUDIT_CALLBACK = 'evennia.contrib.security.auditing.outputs.to_file'
|
|
||||||
|
|
||||||
# Log user input? Be ethical about this; it will log all private and
|
|
||||||
# public communications between players and/or admins (default: False).
|
|
||||||
AUDIT_IN = False
|
|
||||||
|
|
||||||
# Log server output? This will result in logging of ALL system
|
|
||||||
# messages and ALL broadcasts to connected players, so on a busy game any
|
|
||||||
# broadcast to all users will yield a single event for every connected user!
|
|
||||||
AUDIT_OUT = False
|
|
||||||
|
|
||||||
# The default output is a dict. Do you want to allow key:value pairs with
|
|
||||||
# null/blank values? If you're just writing to disk, disabling this saves
|
|
||||||
# some disk space, but whether you *want* sparse values or not is more of a
|
|
||||||
# consideration if you're shipping logs to a NoSQL/schemaless database.
|
|
||||||
# (default: False)
|
|
||||||
AUDIT_ALLOW_SPARSE = False
|
|
||||||
|
|
||||||
# If you write custom commands that handle sensitive data like passwords,
|
|
||||||
# you must write a regular expression to remove that before writing to log.
|
|
||||||
# AUDIT_MASKS is a list of dictionaries that define the names of commands
|
|
||||||
# and the regexes needed to scrub them.
|
|
||||||
# The system already has defaults to filter out sensitive login/creation
|
|
||||||
# commands in the default command set. Your list of AUDIT_MASKS will be appended
|
|
||||||
# to those defaults.
|
|
||||||
#
|
|
||||||
# In the regex, the sensitive data itself must be captured in a named group with a
|
|
||||||
# label of 'secret' (see the Python docs on the `re` module for more info). For
|
|
||||||
# example: `{'authentication': r"^@auth\s+(?P<secret>[\w]+)"}`
|
|
||||||
AUDIT_MASKS = []
|
|
||||||
|
|
@ -1,60 +0,0 @@
|
||||||
"""
|
|
||||||
Auditable Server Sessions - Example Outputs
|
|
||||||
Example methods demonstrating output destinations for logs generated by
|
|
||||||
audited server sessions.
|
|
||||||
|
|
||||||
This is designed to be a single source of events for developers to customize
|
|
||||||
and add any additional enhancements before events are written out-- i.e. if you
|
|
||||||
want to keep a running list of what IPs a user logs in from on account/character
|
|
||||||
objects, or if you want to perform geoip or ASN lookups on IPs before committing,
|
|
||||||
or tag certain events with the results of a reputational lookup, this should be
|
|
||||||
the easiest place to do it. Write a method and invoke it via
|
|
||||||
`settings.AUDIT_CALLBACK` to have log data objects passed to it.
|
|
||||||
|
|
||||||
Evennia contribution - Johnny 2017
|
|
||||||
"""
|
|
||||||
from evennia.utils.logger import log_file
|
|
||||||
import json
|
|
||||||
import syslog
|
|
||||||
|
|
||||||
|
|
||||||
def to_file(data):
|
|
||||||
"""
|
|
||||||
Writes dictionaries of data generated by an AuditedServerSession to files
|
|
||||||
in JSON format, bucketed by date.
|
|
||||||
|
|
||||||
Uses Evennia's native logger and writes to the default
|
|
||||||
log directory (~/yourgame/server/logs/ or settings.LOG_DIR)
|
|
||||||
|
|
||||||
Args:
|
|
||||||
data (dict): Parsed session transmission data.
|
|
||||||
|
|
||||||
"""
|
|
||||||
# Bucket logs by day and remove objects before serialization
|
|
||||||
bucket = data.pop("objects")["time"].strftime("%Y-%m-%d")
|
|
||||||
|
|
||||||
# Write it
|
|
||||||
log_file(json.dumps(data), filename="audit_%s.log" % bucket)
|
|
||||||
|
|
||||||
|
|
||||||
def to_syslog(data):
|
|
||||||
"""
|
|
||||||
Writes dictionaries of data generated by an AuditedServerSession to syslog.
|
|
||||||
|
|
||||||
Takes advantage of your system's native logger and writes to wherever
|
|
||||||
you have it configured, which is independent of Evennia.
|
|
||||||
Linux systems tend to write to /var/log/syslog.
|
|
||||||
|
|
||||||
If you're running rsyslog, you can configure it to dump and/or forward logs
|
|
||||||
to disk and/or an external data warehouse (recommended-- if your server is
|
|
||||||
compromised or taken down, losing your logs along with it is no help!).
|
|
||||||
|
|
||||||
Args:
|
|
||||||
data (dict): Parsed session transmission data.
|
|
||||||
|
|
||||||
"""
|
|
||||||
# Remove objects before serialization
|
|
||||||
data.pop("objects")
|
|
||||||
|
|
||||||
# Write it out
|
|
||||||
syslog.syslog(json.dumps(data))
|
|
||||||
|
|
@ -1,249 +0,0 @@
|
||||||
"""
|
|
||||||
Auditable Server Sessions:
|
|
||||||
Extension of the stock ServerSession that yields objects representing
|
|
||||||
user inputs and system outputs.
|
|
||||||
|
|
||||||
Evennia contribution - Johnny 2017
|
|
||||||
"""
|
|
||||||
import os
|
|
||||||
import re
|
|
||||||
import socket
|
|
||||||
|
|
||||||
from django.utils import timezone
|
|
||||||
from django.conf import settings as ev_settings
|
|
||||||
from evennia.utils import utils, logger, mod_import, get_evennia_version
|
|
||||||
from evennia.server.serversession import ServerSession
|
|
||||||
|
|
||||||
# Attributes governing auditing of commands and where to send log objects
|
|
||||||
AUDIT_CALLBACK = getattr(
|
|
||||||
ev_settings, "AUDIT_CALLBACK", "evennia.contrib.security.auditing.outputs.to_file"
|
|
||||||
)
|
|
||||||
AUDIT_IN = getattr(ev_settings, "AUDIT_IN", False)
|
|
||||||
AUDIT_OUT = getattr(ev_settings, "AUDIT_OUT", False)
|
|
||||||
AUDIT_ALLOW_SPARSE = getattr(ev_settings, "AUDIT_ALLOW_SPARSE", False)
|
|
||||||
AUDIT_MASKS = [
|
|
||||||
{"connect": r"^[@\s]*[connect]{5,8}\s+(\".+?\"|[^\s]+)\s+(?P<secret>.+)"},
|
|
||||||
{"connect": r"^[@\s]*[connect]{5,8}\s+(?P<secret>[\w]+)"},
|
|
||||||
{"create": r"^[^@]?[create]{5,6}\s+(\w+|\".+?\")\s+(?P<secret>[\w]+)"},
|
|
||||||
{"create": r"^[^@]?[create]{5,6}\s+(?P<secret>[\w]+)"},
|
|
||||||
{"userpassword": r"^[@\s]*[userpassword]{11,14}\s+(\w+|\".+?\")\s+=*\s*(?P<secret>[\w]+)"},
|
|
||||||
{"userpassword": r"^.*new password set to '(?P<secret>[^']+)'\."},
|
|
||||||
{"userpassword": r"^.* has changed your password to '(?P<secret>[^']+)'\."},
|
|
||||||
{"password": r"^[@\s]*[password]{6,9}\s+(?P<secret>.*)"},
|
|
||||||
] + getattr(ev_settings, "AUDIT_MASKS", [])
|
|
||||||
|
|
||||||
|
|
||||||
if AUDIT_CALLBACK:
|
|
||||||
try:
|
|
||||||
AUDIT_CALLBACK = getattr(
|
|
||||||
mod_import(".".join(AUDIT_CALLBACK.split(".")[:-1])), AUDIT_CALLBACK.split(".")[-1]
|
|
||||||
)
|
|
||||||
logger.log_sec("Auditing module online.")
|
|
||||||
logger.log_sec(
|
|
||||||
"Audit record User input: {}, output: {}.\n"
|
|
||||||
"Audit sparse recording: {}, Log callback: {}".format(
|
|
||||||
AUDIT_IN, AUDIT_OUT, AUDIT_ALLOW_SPARSE, AUDIT_CALLBACK
|
|
||||||
)
|
|
||||||
)
|
|
||||||
except Exception as e:
|
|
||||||
logger.log_err("Failed to activate Auditing module. %s" % e)
|
|
||||||
|
|
||||||
|
|
||||||
class AuditedServerSession(ServerSession):
|
|
||||||
"""
|
|
||||||
This particular implementation parses all server inputs and/or outputs and
|
|
||||||
passes a dict containing the parsed metadata to a callback method of your
|
|
||||||
creation. This is useful for recording player activity where necessary for
|
|
||||||
security auditing, usage analysis or post-incident forensic discovery.
|
|
||||||
|
|
||||||
*** WARNING ***
|
|
||||||
All strings are recorded and stored in plaintext. This includes those strings
|
|
||||||
which might contain sensitive data (create, connect, @password). These commands
|
|
||||||
have their arguments masked by default, but you must mask or mask any
|
|
||||||
custom commands of your own that handle sensitive information.
|
|
||||||
|
|
||||||
See README.md for installation/configuration instructions.
|
|
||||||
"""
|
|
||||||
|
|
||||||
def audit(self, **kwargs):
|
|
||||||
"""
|
|
||||||
Extracts messages and system data from a Session object upon message
|
|
||||||
send or receive.
|
|
||||||
|
|
||||||
Keyword Args:
|
|
||||||
src (str): Source of data; 'client' or 'server'. Indicates direction.
|
|
||||||
text (str or list): Client sends messages to server in the form of
|
|
||||||
lists. Server sends messages to client as string.
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
log (dict): Dictionary object containing parsed system and user data
|
|
||||||
related to this message.
|
|
||||||
|
|
||||||
"""
|
|
||||||
# Get time at start of processing
|
|
||||||
time_obj = timezone.now()
|
|
||||||
time_str = str(time_obj)
|
|
||||||
|
|
||||||
session = self
|
|
||||||
src = kwargs.pop("src", "?")
|
|
||||||
bytecount = 0
|
|
||||||
|
|
||||||
# Do not log empty lines
|
|
||||||
if not kwargs:
|
|
||||||
return {}
|
|
||||||
|
|
||||||
# Get current session's IP address
|
|
||||||
client_ip = session.address
|
|
||||||
|
|
||||||
# Capture Account name and dbref together
|
|
||||||
account = session.get_account()
|
|
||||||
account_token = ""
|
|
||||||
if account:
|
|
||||||
account_token = "%s%s" % (account.key, account.dbref)
|
|
||||||
|
|
||||||
# Capture Character name and dbref together
|
|
||||||
char = session.get_puppet()
|
|
||||||
char_token = ""
|
|
||||||
if char:
|
|
||||||
char_token = "%s%s" % (char.key, char.dbref)
|
|
||||||
|
|
||||||
# Capture Room name and dbref together
|
|
||||||
room = None
|
|
||||||
room_token = ""
|
|
||||||
if char:
|
|
||||||
room = char.location
|
|
||||||
room_token = "%s%s" % (room.key, room.dbref)
|
|
||||||
|
|
||||||
# Try to compile an input/output string
|
|
||||||
def drill(obj, bucket):
|
|
||||||
if isinstance(obj, dict):
|
|
||||||
return bucket
|
|
||||||
elif utils.is_iter(obj):
|
|
||||||
for sub_obj in obj:
|
|
||||||
bucket.extend(drill(sub_obj, []))
|
|
||||||
else:
|
|
||||||
bucket.append(obj)
|
|
||||||
return bucket
|
|
||||||
|
|
||||||
text = kwargs.pop("text", "")
|
|
||||||
if utils.is_iter(text):
|
|
||||||
text = "|".join(drill(text, []))
|
|
||||||
|
|
||||||
# Mask any PII in message, where possible
|
|
||||||
bytecount = len(text.encode("utf-8"))
|
|
||||||
text = self.mask(text)
|
|
||||||
|
|
||||||
# Compile the IP, Account, Character, Room, and the message.
|
|
||||||
log = {
|
|
||||||
"time": time_str,
|
|
||||||
"hostname": socket.getfqdn(),
|
|
||||||
"application": "%s" % ev_settings.SERVERNAME,
|
|
||||||
"version": get_evennia_version(),
|
|
||||||
"pid": os.getpid(),
|
|
||||||
"direction": "SND" if src == "server" else "RCV",
|
|
||||||
"protocol": self.protocol_key,
|
|
||||||
"ip": client_ip,
|
|
||||||
"session": "session#%s" % self.sessid,
|
|
||||||
"account": account_token,
|
|
||||||
"character": char_token,
|
|
||||||
"room": room_token,
|
|
||||||
"text": text.strip(),
|
|
||||||
"bytes": bytecount,
|
|
||||||
"data": kwargs,
|
|
||||||
"objects": {
|
|
||||||
"time": time_obj,
|
|
||||||
"session": self,
|
|
||||||
"account": account,
|
|
||||||
"character": char,
|
|
||||||
"room": room,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
# Remove any keys with blank values
|
|
||||||
if AUDIT_ALLOW_SPARSE is False:
|
|
||||||
log["data"] = {k: v for k, v in log["data"].items() if v}
|
|
||||||
log["objects"] = {k: v for k, v in log["objects"].items() if v}
|
|
||||||
log = {k: v for k, v in log.items() if v}
|
|
||||||
|
|
||||||
return log
|
|
||||||
|
|
||||||
def mask(self, msg):
|
|
||||||
"""
|
|
||||||
Masks potentially sensitive user information within messages before
|
|
||||||
writing to log. Recording cleartext password attempts is bad policy.
|
|
||||||
|
|
||||||
Args:
|
|
||||||
msg (str): Raw text string sent from client <-> server
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
msg (str): Text string with sensitive information masked out.
|
|
||||||
|
|
||||||
"""
|
|
||||||
# Check to see if the command is embedded within server output
|
|
||||||
_msg = msg
|
|
||||||
is_embedded = False
|
|
||||||
match = re.match(".*Command.*'(.+)'.*is not available.*", msg, flags=re.IGNORECASE)
|
|
||||||
if match:
|
|
||||||
msg = match.group(1).replace("\\", "")
|
|
||||||
submsg = msg
|
|
||||||
is_embedded = True
|
|
||||||
|
|
||||||
for mask in AUDIT_MASKS:
|
|
||||||
for command, regex in mask.items():
|
|
||||||
try:
|
|
||||||
match = re.match(regex, msg, flags=re.IGNORECASE)
|
|
||||||
except Exception as e:
|
|
||||||
logger.log_err(regex)
|
|
||||||
logger.log_err(e)
|
|
||||||
continue
|
|
||||||
|
|
||||||
if match:
|
|
||||||
term = match.group("secret")
|
|
||||||
masked = re.sub(term, "*" * len(term.zfill(8)), msg)
|
|
||||||
|
|
||||||
if is_embedded:
|
|
||||||
msg = re.sub(
|
|
||||||
submsg, "%s <Masked: %s>" % (masked, command), _msg, flags=re.IGNORECASE
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
msg = masked
|
|
||||||
|
|
||||||
return msg
|
|
||||||
|
|
||||||
return _msg
|
|
||||||
|
|
||||||
def data_out(self, **kwargs):
|
|
||||||
"""
|
|
||||||
Generic hook for sending data out through the protocol.
|
|
||||||
|
|
||||||
Keyword Args:
|
|
||||||
kwargs (any): Other data to the protocol.
|
|
||||||
|
|
||||||
"""
|
|
||||||
if AUDIT_CALLBACK and AUDIT_OUT:
|
|
||||||
try:
|
|
||||||
log = self.audit(src="server", **kwargs)
|
|
||||||
if log:
|
|
||||||
AUDIT_CALLBACK(log)
|
|
||||||
except Exception as e:
|
|
||||||
logger.log_err(e)
|
|
||||||
|
|
||||||
super(AuditedServerSession, self).data_out(**kwargs)
|
|
||||||
|
|
||||||
def data_in(self, **kwargs):
|
|
||||||
"""
|
|
||||||
Hook for protocols to send incoming data to the engine.
|
|
||||||
|
|
||||||
Keyword Args:
|
|
||||||
kwargs (any): Other data from the protocol.
|
|
||||||
|
|
||||||
"""
|
|
||||||
if AUDIT_CALLBACK and AUDIT_IN:
|
|
||||||
try:
|
|
||||||
log = self.audit(src="client", **kwargs)
|
|
||||||
if log:
|
|
||||||
AUDIT_CALLBACK(log)
|
|
||||||
except Exception as e:
|
|
||||||
logger.log_err(e)
|
|
||||||
|
|
||||||
super(AuditedServerSession, self).data_in(**kwargs)
|
|
||||||
|
|
@ -1,114 +0,0 @@
|
||||||
"""
|
|
||||||
Module containing the test cases for the Audit system.
|
|
||||||
"""
|
|
||||||
|
|
||||||
from anything import Anything
|
|
||||||
from django.test import override_settings
|
|
||||||
from django.conf import settings
|
|
||||||
from evennia.utils.test_resources import EvenniaTest
|
|
||||||
import re
|
|
||||||
|
|
||||||
# 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_IN = True
|
|
||||||
settings.AUDIT_OUT = True
|
|
||||||
settings.AUDIT_ALLOW_SPARSE = True
|
|
||||||
|
|
||||||
# Configure settings to use custom session - TODO: This is bad practice, changing global settings
|
|
||||||
settings.SERVER_SESSION_CLASS = "evennia.contrib.security.auditing.server.AuditedServerSession"
|
|
||||||
|
|
||||||
|
|
||||||
class AuditingTest(EvenniaTest):
|
|
||||||
def test_mask(self):
|
|
||||||
"""
|
|
||||||
Make sure the 'mask' function is properly masking potentially sensitive
|
|
||||||
information from strings.
|
|
||||||
"""
|
|
||||||
safe_cmds = (
|
|
||||||
"/say hello to my little friend",
|
|
||||||
"@ccreate channel = for channeling",
|
|
||||||
"@create/drop some stuff",
|
|
||||||
"@create rock",
|
|
||||||
"@create a pretty shirt : evennia.contrib.clothing.Clothing",
|
|
||||||
"@charcreate johnnyefhiwuhefwhef",
|
|
||||||
'Command "@logout" is not available. Maybe you meant "@color" or "@cboot"?',
|
|
||||||
'/me says, "what is the password?"',
|
|
||||||
"say the password is plugh",
|
|
||||||
# Unfortunately given the syntax, there is no way to discern the
|
|
||||||
# latter of these as sensitive
|
|
||||||
"@create pretty sunset" "@create johnny password123",
|
|
||||||
'{"text": "Command \'do stuff\' is not available. Type "help" for help."}',
|
|
||||||
)
|
|
||||||
|
|
||||||
for cmd in safe_cmds:
|
|
||||||
self.assertEqual(self.session.mask(cmd), cmd)
|
|
||||||
|
|
||||||
unsafe_cmds = (
|
|
||||||
(
|
|
||||||
"something - new password set to 'asdfghjk'.",
|
|
||||||
"something - new password set to '********'.",
|
|
||||||
),
|
|
||||||
(
|
|
||||||
"someone has changed your password to 'something'.",
|
|
||||||
"someone has changed your password to '*********'.",
|
|
||||||
),
|
|
||||||
("connect johnny password123", "connect johnny ***********"),
|
|
||||||
("concnct johnny password123", "concnct johnny ***********"),
|
|
||||||
("concnct johnnypassword123", "concnct *****************"),
|
|
||||||
('connect "johnny five" "password 123"', 'connect "johnny five" **************'),
|
|
||||||
('connect johnny "password 123"', "connect johnny **************"),
|
|
||||||
("create johnny password123", "create johnny ***********"),
|
|
||||||
("@password password1234 = password2345", "@password ***************************"),
|
|
||||||
("@password password1234 password2345", "@password *************************"),
|
|
||||||
("@passwd password1234 = password2345", "@passwd ***************************"),
|
|
||||||
("@userpassword johnny = password234", "@userpassword johnny = ***********"),
|
|
||||||
("craete johnnypassword123", "craete *****************"),
|
|
||||||
(
|
|
||||||
"Command 'conncect teddy teddy' is not available. Maybe you meant \"@encode\"?",
|
|
||||||
"Command 'conncect ******** ********' is not available. Maybe you meant \"@encode\"?",
|
|
||||||
),
|
|
||||||
(
|
|
||||||
"{'text': u'Command \\'conncect jsis dfiidf\\' is not available. Type \"help\" for help.'}",
|
|
||||||
"{'text': u'Command \\'conncect jsis ********\\' is not available. Type \"help\" for help.'}",
|
|
||||||
),
|
|
||||||
)
|
|
||||||
|
|
||||||
for index, (unsafe, safe) in enumerate(unsafe_cmds):
|
|
||||||
self.assertEqual(re.sub(" <Masked: .+>", "", self.session.mask(unsafe)).strip(), safe)
|
|
||||||
|
|
||||||
# Make sure scrubbing is not being abused to evade monitoring
|
|
||||||
secrets = [
|
|
||||||
"say password password password; ive got a secret that i cant explain",
|
|
||||||
"whisper johnny = password\n let's lynch the landlord",
|
|
||||||
"say connect johnny password1234|the secret life of arabia",
|
|
||||||
"@password eval(\"__import__('os').system('clear')\", {'__builtins__':{}})",
|
|
||||||
]
|
|
||||||
for secret in secrets:
|
|
||||||
self.assertEqual(self.session.mask(secret), secret)
|
|
||||||
|
|
||||||
def test_audit(self):
|
|
||||||
"""
|
|
||||||
Make sure the 'audit' function is returning a dictionary based on values
|
|
||||||
parsed from the Session object.
|
|
||||||
"""
|
|
||||||
log = self.session.audit(src="client", text=[["hello"]])
|
|
||||||
obj = {
|
|
||||||
k: v for k, v in log.items() if k in ("direction", "protocol", "application", "text")
|
|
||||||
}
|
|
||||||
self.assertEqual(
|
|
||||||
obj,
|
|
||||||
{
|
|
||||||
"direction": "RCV",
|
|
||||||
"protocol": "telnet",
|
|
||||||
"application": Anything, # this will change if running tests from the game dir
|
|
||||||
"text": "hello",
|
|
||||||
},
|
|
||||||
)
|
|
||||||
|
|
||||||
# Make sure OOB data is being recorded
|
|
||||||
log = self.session.audit(
|
|
||||||
src="client", text="connect johnny password123", prompt="hp=20|st=10|ma=15", pane=2
|
|
||||||
)
|
|
||||||
self.assertEqual(log["text"], "connect johnny ***********")
|
|
||||||
self.assertEqual(log["data"]["prompt"], "hp=20|st=10|ma=15")
|
|
||||||
self.assertEqual(log["data"]["pane"], 2)
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue