New drop:holds() lock default to limit dropping nonsensical things. Access check
defaults to True for backwards-compatibility in 0.9, will be False in 1.0
diff --git a/docs/1.0-dev/Coding/Unit-Testing.html b/docs/1.0-dev/Coding/Unit-Testing.html
index c35a77d7d2..25957484c5 100644
--- a/docs/1.0-dev/Coding/Unit-Testing.html
+++ b/docs/1.0-dev/Coding/Unit-Testing.html
@@ -261,7 +261,7 @@ connecting to the game. It is used by
EvenniaCommandTest - has the same environment like EvenniaTest but also adds a special
-.call() method specifically for
+.call() method specifically for
testing Evennia Commands. It allows you to compare what the command actually
returns to the player with what you expect. Read the call api doc for more info.
EvenniaTestCase - This is identical to the regular Python TestCase class, it’s
@@ -306,7 +306,7 @@ just there for naming symmetry with <
of it and if it matches, that’s enough. Use \n to denote line breaks and (this is a special for
the .call helper), || to indicate multiple uses of .msg() in the Command. The .call helper
has a lot of arguments for mimicing different ways of calling a Command, so make sure to
-read the API docs for .call().
This page describes how to install and run the Evennia server on an Android phone. This will involve
-installing a slew of third-party programs from the Google Play store, so make sure you are okay with
-this before starting.
+
This page describes how to install and run the Evennia server on an Android phone. This will involve installing a slew of third-party programs from the Google Play store, so make sure you are okay with this before starting.
Warning
Android installation is experimental and not tested with later versions of Android.
@@ -124,17 +122,9 @@ Report your findings.
The first thing to do is install a terminal emulator that allows a “full” version of linux to be
-run. Note that Android is essentially running on top of linux so if you have a rooted phone, you may
-be able to skip this step. You don’t require a rooted phone to install Evennia though.
-
Assuming we do not have root, we will install
-Termux.
-Termux provides a base installation of Linux essentials, including apt and Python, and makes them
-available under a writeable directory. It also gives us a terminal where we can enter commands. By
-default, Android doesn’t give you permissions to the root folder, so Termux pretends that its own
-installation directory is the root directory.
-
Termux will set up a base system for us on first launch, but we will need to install some
-prerequisites for Evennia. Commands you should run in Termux will look like this:
+
The first thing to do is install a terminal emulator that allows a “full” version of linux to be run. Note that Android is essentially running on top of linux so if you have a rooted phone, you may be able to skip this step. You don’t require a rooted phone to install Evennia though.
+
Assuming we do not have root, we will install Termux. Termux provides a base installation of Linux essentials, including apt and Python, and makes them available under a writeable directory. It also gives us a terminal where we can enter commands. By default, Android doesn’t give you permissions to the root folder, so Termux pretends that its own installation directory is the root directory.
+
Termux will set up a base system for us on first launch, but we will need to install some prerequisites for Evennia. Commands you should run in Termux will look like this:
$ cat file.txt
diff --git a/docs/1.0-dev/Setup/Installation-Docker.html b/docs/1.0-dev/Setup/Installation-Docker.html
index 828f9da480..fd1f7cf119 100644
--- a/docs/1.0-dev/Setup/Installation-Docker.html
+++ b/docs/1.0-dev/Setup/Installation-Docker.html
@@ -151,54 +151,28 @@ instructions (no virtualenv needed).
isolated Linux environment. To exit the container and all processes in there, press Ctrl-D. If you
created a new game folder, you will find that it has appeared on-disk.
-
The game folder or any new files that you created from inside the container will appear as owned
-by root. If you want to edit the files outside of the container you should change the ownership.
-On Linux/Mac you do this with sudochownmyname:myname-Rmygame, where you replace myname with
-your username and mygame with whatever your game folder is named.
+
The game folder or any new files that you created from inside the container will appear as owned by root. If you want to edit the files outside of the container you should change the ownership. On Linux/Mac you do this with sudochownmyname:myname-Rmygame, where you replace myname with your username and mygame with whatever your game folder is named.
Below is an explanation of the dockerrun command we used:
-
dockerrun...evennia/evennia tells us that we want to run a new container based on the
-evennia/evennia docker image. Everything in between are options for this. The evennia/evennia is
-the name of our official docker image on the dockerhub
-repository. If you didn’t do dockerpullevennia/evennia first, the image will be downloaded when running this, otherwise your already
-downloaded version will be used. It contains everything needed to run Evennia.
+
dockerrun...evennia/evennia tells us that we want to run a new container based on the evennia/evennia docker image. Everything in between are options for this. The evennia/evennia is the name of our official docker image on the dockerhub repository. If you didn’t do dockerpullevennia/evennia first, the image will be downloaded when running this, otherwise your already downloaded version will be used. It contains everything needed to run Evennia.
-it has to do with creating an interactive session inside the container we start.
--rm will make sure to delete the container when it shuts down. This is nice to keep things tidy
on your drive.
-
-p4000:4000-p4001:4001-p4002:4002 means that we map ports 4000, 4001 and 4002 from
-inside the docker container to same-numbered ports on our host machine. These are ports for telnet,
-webserver and websockets. This is what allows your Evennia server to be accessed from outside the
-container (such as by your MUD client)!
-
-v$PWD:/usr/src/game mounts the current directory (outside the container) to the path
-/usr/src/gameinside the container. This means that when you edit that path in the container you
-will actually be modifying the “real” place on your hard drive. If you didn’t do this, any changes
-would only exist inside the container and be gone if we create a new one. Note that in linux a
-shortcut for the current directory is $PWD. If you don’t have this for your OS, you can replace it
-with the full path to the current on-disk directory (like C:/Development/evennia/game or wherever
-you want your evennia files to appear).
-
--user$UID:$GID ensures the container’s modifications to $PWD are done with you user and
-group IDs instead of root’s IDs (root is the user running evennia inside the container). This avoids
-having stale .pid files in your filesystem between container reboots which you have to force
-delete with sudormserver/*.pid before each boot.
+
-p4000:4000-p4001:4001-p4002:4002 means that we map ports 4000, 4001 and 4002 from inside the docker container to same-numbered ports on our host machine. These are ports for telnet, webserver and websockets. This is what allows your Evennia server to be accessed from outside the container (such as by your MUD client)!
+
-v$PWD:/usr/src/game mounts the current directory (outside the container) to the path /usr/src/gameinside the container. This means that when you edit that path in the container you will actually be modifying the “real” place on your hard drive. If you didn’t do this, any changes would only exist inside the container and be gone if we create a new one. Note that in linux a shortcut for the current directory is $PWD. If you don’t have this for your OS, you can replace it with the full path to the current on-disk directory (like C:/Development/evennia/game or wherever you want your evennia files to appear).
+
--user$UID:$GID ensures the container’s modifications to $PWD are done with you user and group IDs instead of root’s IDs (root is the user running evennia inside the container). This avoids having stale .pid files in your filesystem between container reboots which you have to force delete with sudormserver/*.pid before each boot.
If you run the docker command given in the previous section from your game dir you can then
easily start Evennia and have a running server without any further fuss.
-
But apart from ease of install, the primary benefit to running an Evennia-based game in a container
-is to simplify its deployment into a public production environment. Most cloud-based hosting
+
But apart from ease of install, the primary benefit to running an Evennia-based game in a container is to simplify its deployment into a public production environment. Most cloud-based hosting
providers these days support the ability to run container-based applications. This makes deploying
-or updating your game as simple as building a new container image locally, pushing it to your Docker
-Hub account, and then pulling from Docker Hub into your AWS/Azure/other docker-enabled hosting
-account. The container eliminates the need to install Python, set up a virtualenv, or run pip to
-install dependencies.
+or updating your game as simple as building a new container image locally, pushing it to your Docker Hub account, and then pulling from Docker Hub into your AWS/Azure/other docker-enabled hosting account. The container eliminates the need to install Python, set up a virtualenv, or run pip to install dependencies.
For remote or automated deployment you may want to start Evennia immediately as soon as the docker
-container comes up. If you already have a game folder with a database set up you can also start the
-docker container and pass commands directly to it. The command you pass will be the main process to
-run in the container. From your game dir, run for example this command:
+
For remote or automated deployment you may want to start Evennia immediately as soon as the docker container comes up. If you already have a game folder with a database set up you can also start the docker container and pass commands directly to it. The command you pass will be the main process to run in the container. From your game dir, run for example this command:
These steps assume that you have created or otherwise obtained a game directory already. First, cd
-to your game dir and create a new empty text file named Dockerfile. Save the following two lines
-into it:
+
These steps assume that you have created or otherwise obtained a game directory already. First, cd to your game dir and create a new empty text file named Dockerfile. Save the following two lines into it:
FROMevennia/evennia:latestENTRYPOINTevenniastart-l
@@ -226,9 +198,7 @@ enter it and run commands).
(don’t forget the period at the end, it will use the Dockerfile from the current location). Here
-mydhaccount is the name of your dockerhub account. If you don’t have a dockerhub account you can
-build the image locally only (name the container whatever you like in that case, like just
-mygame).
+mydhaccount is the name of your dockerhub account. If you don’t have a dockerhub account you can build the image locally only (name the container whatever you like in that case, like just mygame).
Docker images are stored centrally on your computer. You can see which ones you have available
locally with dockerimages. Once built, you have a couple of options to run your game.
@@ -251,9 +221,7 @@ option and just give the following command:
Your game will be downloaded from your docker-hub account and a new container will be built using
-the image and started on the server! If your server environment forces you to use different ports,
-you can just map the normal ports differently in the command above.
+
Your game will be downloaded from your docker-hub account and a new container will be built using the image and started on the server! If your server environment forces you to use different ports, you can just map the normal ports differently in the command above.
Above we added the -d option, which starts the container in daemon mode - you won’t see any
return in the console. You can see it running with dockerps:
$ docker ps
@@ -288,21 +256,16 @@ container will get a new container id to reference.
The evennia/evennia docker image holds the evennia library and all of its dependencies. It also
-has an ONBUILD directive which is triggered during builds of images derived from it. This
-ONBUILD directive handles setting up a volume and copying your game directory code into the proper
-location within the container.
+
The evennia/evennia docker image holds the evennia library and all of its dependencies. It also has an ONBUILD directive which is triggered during builds of images derived from it. This
+ONBUILD directive handles setting up a volume and copying your game directory code into the proper location within the container.
In most cases, the Dockerfile for an Evennia-based game will only need the FROMevennia/evennia:latest directive, and optionally a MAINTAINER directive if you plan to publish
your image on Docker Hub and would like to provide contact info.
A new evennia/evennia image is built automatically whenever there is a new commit to the master
-branch of Evennia. It is possible to create your own custom evennia base docker image based on any
-arbitrary commit.
+
A new evennia/evennia image is built automatically whenever there is a new commit to the master branch of Evennia. It is possible to create your own custom evennia base docker image based on any arbitrary commit.
Use git tools to checkout the commit that you want to base your image upon. (In the example
below, we’re checking out commit a8oc3d5b.)
@@ -329,12 +292,9 @@ to be:
FROMmydhacct/evennia:latest
-
Note: From this point, you can also use the dockertag command to set a specific tag on your image
-and/or upload it into Docker Hub under your account.
-
-
At this point, build your game using the same dockerbuild command as usual. Change your
-working directory to be your game directory and run
-
+
Note: From this point, you can also use the dockertag command to set a specific tag on your image and/or upload it into Docker Hub under your account.
+5. At this point, build your game using the same dockerbuild command as usual. Change your
+working directory to be your game directory and run
dockerbuild-tmydhaccountt/mygame.
@@ -342,10 +302,7 @@ working directory to be your game directory and run
The Docker ecosystem includes a tool called docker-compose, which can orchestrate complex multi-
-container applications, or in our case, store the default port and terminal parameters that we want
-specified every time we run our container. A sample docker-compose.yml file to run a containerized
-Evennia game in development might look like this:
+
The Docker ecosystem includes a tool called docker-compose, which can orchestrate complex multi- container applications, or in our case, store the default port and terminal parameters that we want specified every time we run our container. A sample docker-compose.yml file to run a containerized Evennia game in development might look like this:
Note that with this setup you lose the --user$UID option. The problem is that the variable
-UID is not available inside the configuration file docker-compose.yml. A workaround is to
-hardcode your user and group id. In a terminal run echo$UID:$GID and if for example you get
-1000:1000 you can add to docker-compose.yml a line user:1000:1000 just below the image:...
-line.
+
Note that with this setup you lose the --user$UID option. The problem is that the variable UID is not available inside the configuration file docker-compose.yml. A workaround is to hardcode your user and group id. In a terminal run echo$UID:$GID and if for example you get 1000:1000 you can add to docker-compose.yml a line user:1000:1000 just below the image:... line.
@@ -143,6 +144,26 @@ the 0.9.5 version. To install 1.0-dev, you need to add a step http://localhost:4001 or a MUD telnet client to localhost:4000 (use 127.0.0.1 if your OS does
not recognize localhost).
+
+
A Python virtual environment allows you to install Evennia in its own little folder, separate from the rest of the system. You also won’t need any extra permissions. It’s optional to use a virtualenv, but it’s highly recommended. Python supports this natively:
+
python3.11 -m venv evenv
+
+
+
This will create a new folder evenv in your current directory.
+Activate it like this:
The text (evenv) should appear next to your prompt to show that the virtual
+environment is active. You don’t need to actually be in or near the evenv folder for
+the environment to be active.
+
+
Remember that you need to re-activate the virtualenv like this every time you
+start a new terminal/console to get access to the Python packages (notably the
+important evennia program) you installed in the virtualenv!
For Debian-derived systems (like Ubuntu, Mint etc), start a terminal and
@@ -163,36 +184,14 @@ development:
A new folder evennia will appear containing the Evennia library. This only
-contains the source code though, it is not installed yet. To isolate the
-Evennia install and its dependencies from the rest of the system, it is good
-Python practice to install into a virtualenv. If you are unsure about what a
-virtualenv is and why it’s useful, see the Glossary entry on
-virtualenv.
-
python3.10-mvenvevenv
-
-
-
A new folder evenv will appear (we could have called it anything). This
-folder will hold a self-contained setup of Python packages without interfering
-with default Python packages on your system (or the Linux distro lagging behind
-on Python package versions). It will also always use the right version of Python.
-Activate the virtualenv:
-
sourceevenv/bin/activate
-
-
-
The text (evenv) should appear next to your prompt to show that the virtual
-environment is active.
-
-
Remember that you need to activate the virtualenv like this every time you
-start a new terminal to get access to the Python packages (notably the
-important evennia program) we are about to install.
-
-
Next, install Evennia into your active virtualenv. Make sure you are standing
-at the top of your mud directory tree (so you see the evennia/ and evenv/
-folders) and run
+contains the source code though, it is not installed yet.
+
At this point it’s now optional but recommended that you initialize and activate a virtualenv.
+
Next, install Evennia (system-wide, or into your active virtualenv). Make sure you are standing
+at the top of your mud directory tree (so you see the evennia/ folder, and likely the evenv virtualenv folder) and do
pipinstall-eevennia
-
Test that you can run the evennia command everywhere while your virtualenv (evenv) is active.
@@ -223,70 +221,39 @@ install gcc and the Python headers.
A new folder evennia will appear containing the Evennia library. This only
-contains the source code though, it is not installed yet. To isolate the
-Evennia install and its dependencies from the rest of the system, it is good
-Python practice to install into a virtualenv. If you are unsure about what a
-virtualenv is and why it’s useful, see the Glossary entry on virtualenv.
-
python3.10-mvenvevenv
-
-
-
A new folder evenv will appear (we could have called it anything). This
-folder will hold a self-contained setup of Python packages without interfering
-with default Python packages on your system. Activate the virtualenv:
-
sourceevenv/bin/activate
-
-
-
The text (evenv) should appear next to your prompt to show the virtual
-environment is active.
-
-
Remember that you need to activate the virtualenv like this every time you
-start a new terminal to get access to the Python packages (notably the
-important evennia program) we are about to install.
-
-
Next, install Evennia into your active virtualenv. Make sure you are standing
-at the top of your mud directory tree (so you see the evennia/ and evenv/
-folders) and run
+contains the source code though, it is not installed yet.
+
At this point it’s now optional but recommended that you initialize and activate a virtualenv.
+
Next, install Evennia (system-wide, or into your active virtualenv). Make sure you are standing
+at the top of your mud directory tree (so you see the evennia/, and likely the evenv virtualenv
+folder) and do
pipinstall--upgradepip# Old pip versions may be an issue on Mac.pipinstall--upgradesetuptools# Ditto concerning Mac issues.pipinstall-eevennia
-
Test that you can run the evennia command everywhere while your virtualenv (evenv) is active.
If you are running Windows10, consider using the Windows Subsystem for Linux
-(WSL) instead.
-Just set up WSL with an Ubuntu image and follow the Linux install instructions above.
+(WSL) instead. Just set up WSL with an Ubuntu image and follow the Linux install instructions above.
The Evennia server itself is a command line program. In the Windows launch
menu, start All Programs -> Accessories -> command prompt and you will get
-the Windows command line interface. Here is one of many tutorials on using the Windows command
-line
-if you are unfamiliar with it.
Install Python from the Python homepage. You will
-need to be a
-Windows Administrator to install packages. Get Python any version of Python 3.10, usually
+
Install Python from the Python homepage. You will need to be a
+Windows Administrator to install packages. Get Python 3.11, usually
the 64-bit version (although it doesn’t matter too much). When installing, make sure
to check-mark all install options, especially the one about making Python
available on the path (you may have to scroll to see it). This allows you to
-just write python in any console without first finding where the python
+just write python (or possibly py) in any console without first finding where the python
program actually sits on your hard drive.
-
You need to also get GIT and install it. You
-can use the default install options but when you get asked to “Adjust your PATH
-environment”, you should select the second option “Use Git from the Windows
-Command Prompt”, which gives you more freedom as to where you can use the
-program.
-
Finally you must install the Microsoft Visual C++ compiler for
-Python. Download and run the linked installer and
-install the C++ tools. Keep all the defaults. Allow the install of the “Win10 SDK”, even if you are
-on Win7 (not tested on older Windows versions). If you later have issues with installing Evennia due
-to a failure to build the “Twisted wheels”, this is where you are missing things.
-
You may need the pypiwin32 Python headers. Install
-these only if you have issues.
+
You need to also get GIT and install it. You can use the default install options but when you get asked to “Adjust your PATH environment”, you should select the second option “Use Git from the Windows Command Prompt”, which gives you more freedom as to where you can use the program.
+
Finally you must install the Microsoft Visual C++ compiler for Python. Download and run the linked installer and install the C++ tools. Keep all the defaults. Allow the install of the “Win10 SDK”, even if you are on Win7 (not tested on older Windows versions). If you later have issues with installing Evennia due to a failure to build the “Twisted wheels”, this is where you are missing things.
+
You may need the pypiwin32 Python headers. Install these only if you have issues.
You can install Evennia wherever you want. cd to that location and create a
new folder for all your Evennia development (let’s call it muddev).
@@ -303,33 +270,10 @@ directory change.
A new folder evennia will appear containing the Evennia library. This only
-contains the source code though, it is not installed yet. To isolate the
-Evennia install and its dependencies from the rest of the system, it is good
-Python practice to install into a virtualenv. If you are unsure about what a
-virtualenv is and why it’s useful, see the Glossary entry on virtualenv.
-
python3.10-mvenvevenv
-
-
-
A new folder evenv will appear (we could have called it anything). This
-folder will hold a self-contained setup of Python packages without interfering
-with default Python packages on your system. Activate the virtualenv:
-
# If you are using a standard command prompt, you can use the following:
-evenv\scripts\activate.bat
-
-# If you are using a PS Shell, Git Bash, or other, you can use the following:
-.\evenv\scripts\activate
-
-
-
The text (evenv) should appear next to your prompt to show the virtual
-environment is active.
-
-
Remember that you need to activate the virtualenv like this every time you
-start a new console window if you want to get access to the Python packages
-(notably the important evennia program) we are about to install.
-
-
Next, install Evennia into your active virtualenv. Make sure you are standing
-at the top of your mud directory tree (so you see the evennia and evenv
-folders when you use the dir command) and run
+contains the source code though, it is not installed yet.
+
At this point it’s optional but recommended that you initialize and activate a virtualenv.
+
Next, install Evennia (system wide, or into the virtualenv). Make sure you are standing
+at the top of your mud directory tree (so you see evennia, and likely the evenv virtualenv folder when running the dir command). Then do:
pipinstall-eevennia
diff --git a/docs/1.0-dev/Setup/Installation-Troubleshooting.html b/docs/1.0-dev/Setup/Installation-Troubleshooting.html
index 578321b515..513a042dad 100644
--- a/docs/1.0-dev/Setup/Installation-Troubleshooting.html
+++ b/docs/1.0-dev/Setup/Installation-Troubleshooting.html
@@ -124,33 +124,26 @@ everything in the following sections.
Django (v4.0.1+), be warned that latest dev
-version is usually untested with Evennia.
+
Django (v4.2+), be warned that latest dev version is usually untested with Evennia.
GIT - version control software used if you want to install the sources
-(but also useful to track your own code) - Mac users can use the
-git-osx-installer or the
-MacPorts version.
It’s common to be confused and install Evennia in the wrong location. After following the
-git install instructions, the folder structure should look like this:
+
When doing the Git installation, some may be confused and install Evennia in the wrong location. After following the instructions (and using a virtualenv), the folder structure should look like this:
muddev/evenv/evennia/
@@ -162,14 +155,13 @@ is mygame/server/co
If you get an error when installing Evennia (especially with lines mentioning
-failing to include Python.h) then try sudoapt-getinstallpython3-setuptoolspython3-dev.
-Once installed, run pipinstall-eevennia again.
+failing to include Python.h) then try sudoapt-getinstallpython3-setuptoolspython3-dev. Once installed, run pipinstall-eevennia again.
When doing a git install, some not-updated Linux distributions may give errors
about a too-old setuptools or missing functools. If so, update your environment
with pipinstall--upgradepipwheelsetuptools. Then try pipinstall-eevennia again.
-
One user reported a rare issue on Ubuntu 16 is an install error on installing Twisted; Command"pythonsetup.pyegg_info"failedwitherrorcode1in/tmp/pip-build-vnIFTg/twisted/ with errors
-like distutils.errors.DistutilsError:CouldnotfindsuitabledistributionforRequirement.parse('incremental>=16.10.1'). This appears possible to solve by simply updating Ubuntu
-with sudoapt-getupdate&&sudoapt-getdist-upgrade.
+
One user reported a rare issue on Ubuntu 16 is an install error on installing Twisted; Command"pythonsetup.pyegg_info"failedwitherrorcode1in/tmp/pip-build-vnIFTg/twisted/ with errors like distutils.errors.DistutilsError:CouldnotfindsuitabledistributionforRequirement.parse('incremental>=16.10.1'). This appears possible to solve by simply updating Ubuntu with sudoapt-getupdate&&sudoapt-getdist-upgrade.
Users of Fedora (notably Fedora 24) has reported a gcc error saying the directory
/usr/lib/rpm/redhat/redhat-hardened-cc1 is missing, despite gcc itself being installed. The
-confirmed work-around seems to be to
-install the redhat-rpm-config package with e.g. sudodnfinstallredhat-rpm-config.
+confirmed work-around seems to be to install the redhat-rpm-config package with e.g. sudodnfinstallredhat-rpm-config.
Some users trying to set up a virtualenv on an NTFS filesystem find that it fails due to issues
-with symlinks not being supported. Answer is to not use NTFS (seriously, why would you do that to
-yourself?)
+with symlinks not being supported. Answer is to not use NTFS (seriously, why would you do that to yourself?)
Mac users have reported a critical MemoryError when trying to start Evennia on Mac with a Python
-version below 2.7.12. If you get this error, update to the latest XCode and Python2 version.
-
Some Mac users have reported not being able to connect to localhost (i.e. your own computer). If
-so, try to connect to 127.0.0.1 instead, which is the same thing. Use port 4000 from mud clients
-and port 4001 from the web browser as usual.
+
Some Mac users have reported not being able to connect to localhost (i.e. your own computer). If so, try to connect to 127.0.0.1 instead, which is the same thing. Use port 4000 from mud clients and port 4001 from the web browser as usual.
Install Python from the Python homepage. You will
-need to be a Windows Administrator to install packages.
-
When installing Python, make sure to check-mark all install options, especially the one about making Python
-available on the path (you may have to scroll to see it). This allows you to
-just write python in any console without first finding where the python
-program actually sits on your hard drive.
-
If you get a commandnotfound when trying to run the evennia program after installation, try closing the
-Console and starting it again (remember to re-activate the virtualenv!). Sometimes Windows is not updating
-its environment properly.
+
Install Python from the Python homepage. You will need to be a Windows Administrator to install packages.
+
When installing Python, make sure to check-mark all install options, especially the one about making Python available on the path (you may have to scroll to see it). This allows you to
+just write python in any console without first finding where the python program actually sits on your hard drive.
+
If you get a commandnotfound when trying to run the evennia program after installation, try closing the Console and starting it again (remember to re-activate the virtualenv if you use one!). Sometimes Windows is not updating its environment properly and evennia will be available only in the new console.
If you installed Python but the python command is not available (even in a new console), then
-you might have missed installing Python on the path. In the Windows Python installer you get a list
-of options for what to install. Most or all options are pre-checked except this one, and you may
-even have to scroll down to see it. Reinstall Python and make sure it’s checked.
+you might have missed installing Python on the path. In the Windows Python installer you get a list of options for what to install. Most or all options are pre-checked except this one, and you may even have to scroll down to see it. Reinstall Python and make sure it’s checked.
If your MUD client cannot connect to localhost:4000, try the equivalent 127.0.0.1:4000
instead. Some MUD clients on Windows does not appear to understand the alias localhost.
Some Windows users get an error installing the Twisted ‘wheel’. A wheel is a pre-compiled binary
-package for Python. A common reason for this error is that you are using a 32-bit version of Python,
-but Twisted has not yet uploaded the latest 32-bit wheel. Easiest way to fix this is to install a
-slightly older Twisted version. So if, say, version 22.1 failed, install 22.0 manually with pipinstalltwisted==22.0. Alternatively you could check that you are using the 64-bit version of Python
-and uninstall any 32bit one. If so, you must then deactivate the virtualenv, delete the evenv folder
-and recreate it anew with your new Python.
+package for Python. A common reason for this error is that you are using a 32-bit version of Python, but Twisted has not yet uploaded the latest 32-bit wheel. Easiest way to fix this is to install a slightly older Twisted version. So if, say, version 22.1 failed, install 22.0 manually with pipinstalltwisted==22.0. Alternatively you could check that you are using the 64-bit version of Python and uninstall any 32bit one. If so, you must then deactivate the virtualenv, delete the evenv folder and recreate it anew with your new Python.
If your server won’t start, with no error messages (and no log files at all when starting from
-scratch), try to start with evenniaipstart instead. If you then see an error about systemcannotfindthepathspecified, it may be that the file evennia\evennia\server\twistd.bat has the wrong
-path to the twistd executable. This file is auto-generated, so try to delete it and then run
-evenniastart to rebuild it and see if it works. If it still doesn’t work you need to open it in a
-text editor like Notepad. It’s just one line containing the path to the twistd.exe executable as
-determined by Evennia. If you installed Twisted in a non-standard location this might be wrong and
-you should update the line to the real location.
+scratch), try to start with evenniaipstart instead. If you then see an error about systemcannotfindthepathspecified, it may be that the file evennia\evennia\server\twistd.bat has the wrong path to the twistd executable. This file is auto-generated, so try to delete it and then run evenniastart to rebuild it and see if it works. If it still doesn’t work you need to open it in a text editor like Notepad. It’s just one line containing the path to the twistd.exe executable as determined by Evennia. If you installed Twisted in a non-standard location this might be wrong and you should update the line to the real location.
Some users have reported issues with Windows WSL and anti-virus software during Evennia
-development. Timeout errors and the inability to run evenniaconnections may be due to your anti-
-virus software interfering. Try disabling or changing your anti-virus software settings.
+development. Timeout errors and the inability to run evenniaconnections may be due to your anti-virus software interfering. Try disabling or changing your anti-virus software settings.
This is relevant to you already having code in an older Evennia version. If you are new, or don’t have much code yet, it may be easier to just start fresh with the Installation instructions and copy
+over things manually.
Prior to 1.0, all Evennia installs were Git-installs. These instructions
assume that you have a cloned evennia repo and use a virtualenv (best practices).
Make sure to stop Evennia 0.9.5 entirely with evenniastop.
deactivate to leave your active virtualenv.
Make a backup of your entire mygame folder, just to be sure!
-
Delete the old evenv folder, or rename it (in case you want to keep using 0.9.5 for a while).
-
Install Python 3.10 (recommended) or 3.9. Follow the Git-installation for your OS if needed.
-
If using virtualenv, make a new one with python3.10-mvenvevenv, then activate with sourceevenv/bin/activate
-(linux/mac) or \evenv\Script\activate (windows)
-
cd into your evennia/ folder (you want to see the docs/, bin/ directories as well as a nested evennia/ folder)
-
Prior to 1.0 release only - do gitcheckoutdevelop to switch to the develop branch. After release, this will
-be found on the default master branch.
+
Install Python 3.11 (recommended). Follow the Git-installation for your OS if needed.
+
Delete the old virtualenv evenv folder, or rename it (in case you want to keep using 0.9.5 for a while).
If you don’t have anything you want to keep in your existing game dir, you can just start a new onew
using the normal install instructions. If you want to keep/convert your existing
game dir, continue below.
First, make a backup of your exising game dir! If you use version control, make sure to commit your current state.
cd to your existing 0.9.5-based game folder (like mygame.)
-
If you have changed mygame/web, rename the folder to web_0.9.5. If you didn’t change anything (or don’t have
-anything you want to keep), you can delete it entirely.
-
Copy evennia/evennia/game_template/web to mygame/ (e.g. using cp-Rf or a file manager). This new web folder
-replaces the old one and has a very different structure.
+
If you have changed mygame/web, rename the folder to web_0.9.5. If you didn’t change anything (or don’t have anything you want to keep), you can delete it entirely.
+
Copy evennia/evennia/game_template/web to mygame/ (e.g. using cp-Rf or a file manager). This new web folder replaces the old one and has a very different structure.
It’s possible you need to replace/comment out import and calls to the deprecated
-django.conf.urls. The new way to call it is
-available here.
If you made extensive work in your game dir, you may well find that you need to do some (hopefully minor)
-changes to your code before it will start with Evennia 1.0. Some important points:
+
If you made extensive work in your game dir, you may well find that you need to do some (hopefully minor) changes to your code before it will start with Evennia 1.0. Some important points:
-
The evennia/contrib/ folder changed structure - there are now categorized sub-folders, so you have to update
-your imports.
+
The evennia/contrib/ folder changed structure - there are now categorized sub-folders, so you have to update your imports.
Any web changes need to be moved back from your backup into the new structure of web/ manually.
pip install evennia is not yet available in develop branch. Use the git installation.
-
Important
-
If you are converting an existing game from a previous version, see here.
+
If you are converting an existing game from a previous Evennia version, see here.
+
Installing Evennia doesn’t make anything visible online. Apart from installation and updating, you can develop your game without any internet connection.
Installing doesn’t make anything visible online. Apart from installation and updating, you can develop your game without any internet connection.
+
Optional: If you use a contrib that warns you that it needs additional packages, you can
+install all extra dependencies with
+
pip install evennia[extra]
+
+
+
To update Evennia later, do
+
pip install --upgrade evennia
+
+
Once installed, make sure the evennia command works. Use evennia-h for usage help. If you are using a virtualenv, make sure it’s active whenever you need to use the evennia command.
You can optionally let the world know that you are working on a new Evennia-based game by
+
You can let the world know that you are working on a new Evennia-based game by
registering your server with the Evennia game index. You don’t have to be
open for players to do this - you just mark your game as closed and “pre-alpha”.
-# -*- coding: utf-8 -*-
-"""
- ** OBS - this is not a normal command module! **
- ** You cannot import anything in this module as a command! **
-
-This is part of the Evennia unittest framework, for testing the
-stability and integrity of the codebase during updates. This module
-test the default command set. It is instantiated by the
-evennia/objects/tests.py module, which in turn is run by as part of the
-main test suite started with
- > python game/manage.py test.
-
-"""
-importdatetime
-fromunittest.mockimportMagicMock,Mock,patch
-
-fromanythingimportAnything
-fromdjango.confimportsettings
-fromdjango.testimportoverride_settings
-fromparameterizedimportparameterized
-fromtwisted.internetimporttask
-
-fromevenniaimport(
- DefaultCharacter,
- DefaultExit,
- DefaultObject,
- DefaultRoom,
- ObjectDB,
- search_object,
-)
-fromevennia.commandsimportcmdparser
-fromevennia.commands.cmdsetimportCmdSet
-fromevennia.commands.commandimportCommand,InterruptCommand
-fromevennia.commands.defaultimport(
- account,
- admin,
- batchprocess,
- building,
- comms,
- general,
-)
-fromevennia.commands.defaultimporthelpashelp_module
-fromevennia.commands.defaultimportsyscommands,system,unloggedin
-fromevennia.commands.default.cmdset_characterimportCharacterCmdSet
-fromevennia.commands.default.muxcommandimportMuxCommand
-fromevennia.prototypesimportprototypesasprotlib
-fromevennia.server.sessionhandlerimportSESSIONS
-fromevennia.utilsimportcreate,gametime,utils
-fromevennia.utils.test_resourcesimportBaseEvenniaCommandTest# noqa
-fromevennia.utils.test_resourcesimportBaseEvenniaTest,EvenniaCommandTest
-
-# ------------------------------------------------------------
-# Command testing
-# ------------------------------------------------------------
-
-
-
[docs]defsetUp(self):
- super().setUp()
- # we need to set up a logger here since lunr takes over the logger otherwise
- importlogging
-
- logging.basicConfig(level=logging.ERROR)
[docs]deftest_set_help(self):
- self.call(
- help_module.CmdSetHelp(),
- "testhelp, General = This is a test",
- "Topic 'testhelp' was successfully created.",
- cmdset=CharacterCmdSet(),
- )
- self.call(help_module.CmdHelp(),"testhelp","Help for testhelp",cmdset=CharacterCmdSet())
-
- @parameterized.expand(
- [
- (
- "test",# main help entry
- "Help for test\n\n"
- "Main help text\n\n"
- "Subtopics:\n"
- " test/creating extra stuff"
- " test/something else"
- " test/more",
- ),
- (
- "test/creating extra stuff",# subtopic, full match
- "Help for test/creating extra stuff\n\n"
- "Help on creating extra stuff.\n\n"
- "Subtopics:\n"
- " test/creating extra stuff/subsubtopic\n",
- ),
- (
- "test/creating",# startswith-match
- "Help for test/creating extra stuff\n\n"
- "Help on creating extra stuff.\n\n"
- "Subtopics:\n"
- " test/creating extra stuff/subsubtopic\n",
- ),
- (
- "test/extra",# partial match
- "Help for test/creating extra stuff\n\n"
- "Help on creating extra stuff.\n\n"
- "Subtopics:\n"
- " test/creating extra stuff/subsubtopic\n",
- ),
- (
- "test/extra/subsubtopic",# partial subsub-match
- "Help for test/creating extra stuff/subsubtopic\n\nA subsubtopic text",
- ),
- (
- "test/creating extra/subsub",# partial subsub-match
- "Help for test/creating extra stuff/subsubtopic\n\nA subsubtopic text",
- ),
- ("test/Something else","Help for test/something else\n\nSomething else"),# case
- (
- "test/More",# case
- "Help for test/more\n\nAnother text\n\nSubtopics:\n test/more/second-more",
- ),
- (
- "test/More/Second-more",
- "Help for test/more/second-more\n\n"
- "The Second More text.\n\n"
- "Subtopics:\n"
- " test/more/second-more/more again"
- " test/more/second-more/third more",
- ),
- (
- "test/More/-more",# partial match
- "Help for test/more/second-more\n\n"
- "The Second More text.\n\n"
- "Subtopics:\n"
- " test/more/second-more/more again"
- " test/more/second-more/third more",
- ),
- (
- "test/more/second/more again",
- "Help for test/more/second-more/more again\n\nEven more text.\n",
- ),
- (
- "test/more/second/third",
- "Help for test/more/second-more/third more\n\nThird more text\n",
- ),
- ]
- )
- deftest_subtopic_fetch(self,helparg,expected):
- """
- Check retrieval of subtopics.
-
- """
-
- classTestCmd(Command):
- """
- Main help text
-
- # SUBTOPICS
-
- ## creating extra stuff
-
- Help on creating extra stuff.
-
- ### subsubtopic
-
- A subsubtopic text
-
- ## Something else
-
- Something else
-
- ## More
-
- Another text
-
- ### Second-More
-
- The Second More text.
-
- #### More again
-
- Even more text.
-
- #### Third more
-
- Third more text
-
- """
-
- key="test"
-
- classTestCmdSet(CmdSet):
- defat_cmdset_creation(self):
- self.add(TestCmd())
- self.add(help_module.CmdHelp())
-
- self.call(help_module.CmdHelp(),helparg,expected,cmdset=TestCmdSet())
[docs]deftest_py(self):
- # we are not testing CmdReload, CmdReset and CmdShutdown, CmdService or CmdTime
- # since the server is not running during these tests.
- self.call(system.CmdPy(),"1+2",">>> 1+2|3")
- self.call(system.CmdPy(),"/clientraw 1+2",">>> 1+2|3")
[docs]deftest_call(self):
- args=f"/call {self.task.get_id()}"
- wanted_msg="Call task 1 with completion date"
- cmd_result=self.call(system.CmdTasks(),args,wanted_msg)
- self.assertRegex(cmd_result," \(func_test_cmd_tasks\) ")
- self.char1.execute_cmd("y")
- # make certain the task is still active
- self.assertTrue(self.task.active())
- # go past delay time, the task should call do_task and remove itself after calling.
- self.task_handler.clock.advance(self.timedelay+1)
- self.assertFalse(self.task.exists())
[docs]deftest_func_name_manipulation(self):
- self.task_handler.add(self.timedelay,func_test_cmd_tasks)# add an extra task
- args=f"/remove func_test_cmd_tasks"
- wanted_msg=(
- "Task action remove completed on task ID 1.|The task function remove returned: True|"
- "Task action remove completed on task ID 2.|The task function remove returned: True"
- )
- self.call(system.CmdTasks(),args,wanted_msg)
- self.assertFalse(self.task_handler.tasks)# no tasks should exist.
-
-
[docs]deftest_wrong_func_name(self):
- args=f"/remove intentional_fail"
- wanted_msg="No tasks deferring function name intentional_fail found."
- self.call(system.CmdTasks(),args,wanted_msg)
- self.assertTrue(self.task.active())
-
-
[docs]deftest_no_input(self):
- args=f"/cancel {self.task.get_id()}"
- self.call(system.CmdTasks(),args)
- # task should complete since no input was received
- self.task_handler.clock.advance(self.timedelay+1)
- self.assertFalse(self.task.exists())
[docs]deftest_task_complete_waiting_input(self):
- """Test for task completing while waiting for input."""
- self.call(system.CmdTasks(),f"/cancel {self.task.get_id()}")
- self.task_handler.clock.advance(self.timedelay+1)
- self.char1.msg=Mock()
- self.char1.execute_cmd("y")
- text=""
- for_,_,kwargsinself.char1.msg.mock_calls:
- text+=kwargs.get("text","")
- self.assertEqual(text,"Task completed while waiting for input.")
- self.assertFalse(self.task.exists())
-
-
[docs]deftest_new_task_waiting_input(self):
- """
- Test task completing than a new task with the same ID being made while waitinf for input.
- """
- self.assertTrue(self.task.get_id(),1)
- self.call(system.CmdTasks(),f"/cancel {self.task.get_id()}")
- self.task_handler.clock.advance(self.timedelay+1)
- self.assertFalse(self.task.exists())
- self.task=self.task_handler.add(self.timedelay,func_test_cmd_tasks)
- self.assertTrue(self.task.get_id(),1)
- self.char1.msg=Mock()
- self.char1.execute_cmd("y")
- text=""
- for_,_,kwargsinself.char1.msg.mock_calls:
- text+=kwargs.get("text","")
- self.assertEqual(text,"Task completed while waiting for input.")
-
-
[docs]deftest_misformed_command(self):
- wanted_msg=(
- "Task command misformed.|Proper format tasks[/switch] [function name or task id]"
- )
- self.call(system.CmdTasks(),f"/cancel",wanted_msg)
[docs]deftest_char_create(self):
- self.call(
- account.CmdCharCreate(),
- "Test1=Test char",
- "Created new character Test1. Use ic Test1 to enter the game",
- caller=self.account,
- )
-
-
[docs]deftest_char_delete(self):
- # Chardelete requires user input; this test is mainly to confirm
- # whether permissions are being checked
-
- # Add char to account playable characters
- self.account.db._playable_characters.append(self.char1)
-
- # Try deleting as Developer
- self.call(
- account.CmdCharDelete(),
- "Char",
- "This will permanently destroy 'Char'. This cannot be undone. Continue yes/[no]?",
- caller=self.account,
- )
-
- # Downgrade permissions on account
- self.account.permissions.add("Player")
- self.account.permissions.remove("Developer")
-
- # Set lock on character object to prevent deletion
- self.char1.locks.add("delete:none()")
-
- # Try deleting as Player
- self.call(
- account.CmdCharDelete(),
- "Char",
- "You do not have permission to delete this character.",
- caller=self.account,
- )
-
- # Set lock on character object to allow self-delete
- self.char1.locks.add("delete:pid(%i)"%self.account.id)
-
- # Try deleting as Player again
- self.call(
- account.CmdCharDelete(),
- "Char",
- "This will permanently destroy 'Char'. This cannot be undone. Continue yes/[no]?",
- caller=self.account,
- )
-
-
[docs]deftest_quell(self):
- self.call(
- account.CmdQuell(),
- "",
- "Quelling to current puppet's permissions (developer).",
- caller=self.account,
- )
[docs]deftest_exit_commands(self):
- self.call(
- building.CmdOpen(),"TestExit1=Room2","Created new Exit 'TestExit1' from Room to Room2"
- )
- self.call(building.CmdLink(),"TestExit1=Room","Link created TestExit1 -> Room (one way).")
- self.call(building.CmdUnLink(),"","Usage: ")
- self.call(building.CmdLink(),"NotFound","Could not find 'NotFound'.")
- self.call(building.CmdLink(),"TestExit","TestExit1 is an exit to Room.")
- self.call(building.CmdLink(),"Obj","Obj is not an exit. Its home location is Room.")
- self.call(
- building.CmdUnLink(),"TestExit1","Former exit TestExit1 no longer links anywhere."
- )
-
- self.char1.location=self.room2
- self.call(
- building.CmdOpen(),"TestExit2=Room","Created new Exit 'TestExit2' from Room2 to Room."
- )
- self.call(
- building.CmdOpen(),
- "TestExit2=Room",
- "Exit TestExit2 already exists. It already points to the correct place.",
- )
-
- # ensure it matches locally first
- self.call(
- building.CmdLink(),"TestExit=Room2","Link created TestExit2 -> Room2 (one way)."
- )
- self.call(
- building.CmdLink(),
- "/twoway TestExit={}".format(self.exit.dbref),
- "Link created TestExit2 (in Room2) <-> out (in Room) (two-way).",
- )
- self.call(
- building.CmdLink(),
- "/twoway TestExit={}".format(self.room1.dbref),
- "To create a two-way link, TestExit2 and Room must both have a location ",
- )
- self.call(
- building.CmdLink(),
- "/twoway {}={}".format(self.exit.dbref,self.exit.dbref),
- "Cannot link an object to itself.",
- )
- self.call(building.CmdLink(),"","Usage: ")
- # ensure can still match globally when not a local name
- self.call(building.CmdLink(),"TestExit1=Room2","Note: TestExit1")
- self.call(
- building.CmdLink(),"TestExit1=","Former exit TestExit1 no longer links anywhere."
- )
-
-
[docs]deftest_set_home(self):
- self.call(
- building.CmdSetHome(),"Obj = Room2","Home location of Obj was changed from Room"
- )
- self.call(building.CmdSetHome(),"","Usage: ")
- self.call(building.CmdSetHome(),"self","Char's current home is Room")
- self.call(building.CmdSetHome(),"Obj","Obj's current home is Room2")
- self.obj1.home=None
- self.call(building.CmdSetHome(),"Obj = Room2","Home location of Obj was set to Room")
[docs]@patch("evennia.commands.default.comms.CHANNEL_DEFAULT_TYPECLASS",DefaultChannel)
-classTestCommsChannel(BaseEvenniaCommandTest):
- """
- Test the central `channel` command.
-
- """
-
-
[docs]deftest_page(self):
- self.call(
- comms.CmdPage(),
- "TestAccount2 = Test",
- "TestAccount2 is offline. They will see your message if they list their pages later."
- "|You paged TestAccount2 with: 'Test'.",
- receiver=self.account,
- )
-
-
-
[docs]classTestBatchProcess(BaseEvenniaCommandTest):
- """
- Test the batch processor.
-
- """
-
- # there is some sort of issue with the mock; it needs to loaded once to work
- fromevennia.contrib.tutorials.red_buttonimportred_button# noqa
-
-
[docs]@patch("evennia.contrib.tutorials.red_button.red_button.repeat")
- @patch("evennia.contrib.tutorials.red_button.red_button.delay")
- deftest_batch_commands(self,mock_tutorials,mock_repeat):
- # cannot test batchcode here, it must run inside the server process
- self.call(
- batchprocess.CmdBatchCommands(),
- "batchprocessor.example_batch_cmds",
- "Running Batch-command processor - Automatic mode for"
- " batchprocessor.example_batch_cmds",
- )
- # we make sure to delete the button again here to stop the running reactor
- confirm=building.CmdDestroy.confirm
- building.CmdDestroy.confirm=False
- self.call(building.CmdDestroy(),"button","button was destroyed.")
- building.CmdDestroy.confirm=confirm
- mock_repeat.assert_called()
[docs]deftest_info_command(self):
- # instead of using SERVER_START_TIME (0), we use 86400 because Windows won't let us use anything lower
- gametime.SERVER_START_TIME=86400
- expected=(
- "## BEGIN INFO 1.1\nName: %s\nUptime: %s\nConnected: %d\nVersion: Evennia %s\n## END"
- " INFO"
- %(
- settings.SERVERNAME,
- datetime.datetime.fromtimestamp(gametime.SERVER_START_TIME).ctime(),
- SESSIONS.account_count(),
- utils.get_evennia_version(),
- )
- )
- self.call(unloggedin.CmdUnconnectedInfo(),"",expected)
- delgametime.SERVER_START_TIME
[docs]classTestColorMarkup(BaseEvenniaTest):
- """
- Note: Normally this would be tested by importing the ansi parser and run
- the mappings through it. This is not possible since the ansi module creates
- its mapping at the module/class level; since the ansi module is used by so
- many other modules it appears that trying to overload
- settings to test it causes issues with unrelated tests.
- """
-
-
[docs]deftest_list(self):
- """Test listing callbacks with different rights."""
- table=self.call(CmdCallback(),"out")
- lines=table.splitlines()[3:-1]
- self.assertNotEqual(lines,[])
-
- # Check that the second column only contains 0 (0) (no callback yet)
- forlineinlines:
- cols=line.split("|")
- self.assertIn(cols[2].strip(),("0 (0)",""))
-
- # Add some callback
- self.handler.add_callback(self.exit,"traverse","pass",author=self.char1,valid=True)
-
- # Try to obtain more details on a specific callback on exit
- table=self.call(CmdCallback(),"out = traverse")
- lines=table.splitlines()[3:-1]
- self.assertEqual(len(lines),1)
- line=lines[0]
- cols=line.split("|")
- self.assertIn(cols[1].strip(),("1",""))
- self.assertIn(cols[2].strip(),(str(self.char1),""))
- self.assertIn(cols[-1].strip(),("Yes","No",""))
-
- # Run the same command with char2
- # char2 shouldn't see the last column (Valid)
- table=self.call(CmdCallback(),"out = traverse",caller=self.char2)
- lines=table.splitlines()[3:-1]
- self.assertEqual(len(lines),1)
- line=lines[0]
- cols=line.split("|")
- self.assertEqual(cols[1].strip(),"1")
- self.assertNotIn(cols[-1].strip(),("Yes","No"))
-
- # In any case, display the callback
- # The last line should be "pass" (the callback code)
- details=self.call(CmdCallback(),"out = traverse 1")
- self.assertEqual(details.splitlines()[-1],"pass")
-
-
[docs]deftest_add(self):
- """Test to add an callback."""
- self.call(CmdCallback(),"/add out = traverse")
- editor=self.char1.ndb._eveditor
- self.assertIsNotNone(editor)
-
- # Edit the callback
- editor.update_buffer(
- dedent(
- """
- if character.key == "one":
- character.msg("You can pass.")
- else:
- character.msg("You can't pass.")
- deny()
- """.strip(
- "\n"
- )
- )
- )
- editor.save_buffer()
- editor.quit()
- callback=self.exit.callbacks.get("traverse")[0]
- self.assertEqual(callback.author,self.char1)
- self.assertEqual(callback.valid,True)
- self.assertTrue(len(callback.code)>0)
-
- # We're going to try the same thing but with char2
- # char2 being a player for our test, the callback won't be validated.
- self.call(CmdCallback(),"/add out = traverse",caller=self.char2)
- editor=self.char2.ndb._eveditor
- self.assertIsNotNone(editor)
-
- # Edit the callback
- editor.update_buffer(
- dedent(
- """
- character.msg("No way.")
- """.strip(
- "\n"
- )
- )
- )
- editor.save_buffer()
- editor.quit()
- callback=self.exit.callbacks.get("traverse")[1]
- self.assertEqual(callback.author,self.char2)
- self.assertEqual(callback.valid,False)
- self.assertTrue(len(callback.code)>0)
-
-
[docs]deftest_del(self):
- """Add and remove an callback."""
- self.handler.add_callback(self.exit,"traverse","pass",author=self.char1,valid=True)
-
- # Try to delete the callback
- # char2 shouldn't be allowed to do so (that's not HIS callback)
- self.call(CmdCallback(),"/del out = traverse 1",caller=self.char2)
- self.assertTrue(len(self.handler.get_callbacks(self.exit).get("traverse",[]))==1)
-
- # Now, char1 should be allowed to delete it
- self.call(CmdCallback(),"/del out = traverse 1")
- self.assertTrue(len(self.handler.get_callbacks(self.exit).get("traverse",[]))==0)
-
-
[docs]deftest_lock(self):
- """Test the lock of multiple editing."""
- self.call(CmdCallback(),"/add here = time 8:00",caller=self.char2)
- self.assertIsNotNone(self.char2.ndb._eveditor)
-
- # Now ask char1 to edit
- line=self.call(CmdCallback(),"/edit here = time 1")
- self.assertIsNone(self.char1.ndb._eveditor)
-
- # Try to delete this callback while char2 is editing it
- line=self.call(CmdCallback(),"/del here = time 1")
-
-
[docs]deftest_accept(self):
- """Accept an callback."""
- self.call(CmdCallback(),"/add here = time 8:00",caller=self.char2)
- editor=self.char2.ndb._eveditor
- self.assertIsNotNone(editor)
-
- # Edit the callback
- editor.update_buffer(
- dedent(
- """
- room.msg_contents("It's 8 PM, everybody up!")
- """.strip(
- "\n"
- )
- )
- )
- editor.save_buffer()
- editor.quit()
- callback=self.room1.callbacks.get("time")[0]
- self.assertEqual(callback.valid,False)
-
- # chars shouldn't be allowed to the callback
- self.call(CmdCallback(),"/accept here = time 1",caller=self.char2)
- callback=self.room1.callbacks.get("time")[0]
- self.assertEqual(callback.valid,False)
-
- # char1 will accept the callback
- self.call(CmdCallback(),"/accept here = time 1")
- callback=self.room1.callbacks.get("time")[0]
- self.assertEqual(callback.valid,True)
-
-
-
[docs]classTestDefaultCallbacks(BaseEvenniaCommandTest):
-
- """Test the default callbacks."""
-
-
[docs]deftest_cboot(self):
- # No one else connected to boot
- self.call(
- comms.CmdCBoot(),
- "",
- "Usage: cboot[/quiet] <channel> = <account> [:reason]",
- receiver=self.account,
- )
-
-
[docs]deftest_cdestroy(self):
- self.call(
- comms.CmdCdestroy(),
- "testchan",
- "[testchan] TestAccount: testchan is being destroyed. Make sure to change your aliases."
- "|Channel 'testchan' was destroyed.",
- receiver=self.account,
- )
[docs]definit_parser(self):
- """Fill out options."""
- self.parser.add_argument("nb1",type=int,help="the first number")
- self.parser.add_argument("nb2",type=int,help="the second number")
- self.parser.add_argument("-v","--verbose",action="store_true")
[docs]deftest_success(self):
- """See the command parsing succeed."""
- self.call(CmdDummy(),"5 10","5 * 10 = 50")
- self.call(CmdDummy(),"5 10 -v","5 times 10 is 50")
-
-
[docs]deftest_failure(self):
- """If not provided with the right info, should fail."""
- ret=self.call(CmdDummy(),"5")
- lines=ret.splitlines()
- self.assertTrue(any(lin.startswith("usage:")forlininlines))
- self.assertTrue(any(lin.startswith("dummy: error:")forlininlines))
-
- # If we specify an incorrect number as parameter
- ret=self.call(CmdDummy(),"five ten")
- lines=ret.splitlines()
- self.assertTrue(any(lin.startswith("usage:")forlininlines))
- self.assertTrue(any(lin.startswith("dummy: error:")forlininlines))
[docs]deftest_overwrite(self):
- room=utils.create_evscaperoom_object("evscaperoom.room.EvscapeRoom",key="Testroom")
- obj1=utils.create_evscaperoom_object(
- objects.EvscaperoomObject,key="testobj",location=room
- )
- id1=obj1.id
-
- obj2=utils.create_evscaperoom_object(
- objects.EvscaperoomObject,key="testobj",location=room
- )
- id2=obj2.id
-
- # we should have created a new object, deleting the old same-named one
- self.assertTrue(id1!=id2)
- self.assertFalse(bool(obj1.pk))
- self.assertTrue(bool(obj2.pk))
-
-
[docs]deftest_parse_for_perspectives(self):
-
- second,third=utils.parse_for_perspectives("~You ~look at the nice book","TestGuy")
- self.assertTrue(second,"You look at the nice book")
- self.assertTrue(third,"TestGuy looks at the nice book")
- # irregular
- second,third=utils.parse_for_perspectives("With a smile, ~you ~were gone","TestGuy")
- self.assertTrue(second,"With a smile, you were gone")
- self.assertTrue(third,"With a smile, TestGuy was gone")
-
-
[docs]deftest_parse_for_things(self):
-
- string="Looking at *book and *key."
- self.assertEqual(utils.parse_for_things(string,0),"Looking at book and key.")
- self.assertEqual(utils.parse_for_things(string,1),"Looking at |ybook|n and |ykey|n.")
- self.assertEqual(utils.parse_for_things(string,2),"Looking at |y[book]|n and |y[key]|n.")
[docs]deftest_craft_hook__succeed(self):
- """Test craft hook, the main access method."""
-
- expected_result=_TestMaterial("test_result")
- self.recipe.do_craft=mock.MagicMock(return_value=expected_result)
-
- self.assertTrue(self.recipe.allow_craft)
-
- result=self.recipe.craft()
-
- # check result
- self.assertEqual(result,expected_result)
- self.recipe.do_craft.assert_called_with(kw1=1,kw2=2)
-
- # since allow_reuse is False, this usage should now be turned off
- self.assertFalse(self.recipe.allow_craft)
- # trying to re-run again should fail since rerun is False
- withself.assertRaises(crafting.CraftingError):
- self.recipe.craft()
-
-
[docs]deftest_craft_hook__fail(self):
- """Test failing the call"""
-
- self.recipe.do_craft=mock.MagicMock(return_value=None)
-
- # trigger exception
- withself.assertRaises(crafting.CraftingError):
- self.recipe.craft(raise_exception=True)
-
- # reset and try again without exception
- self.recipe.allow_craft=True
- result=self.recipe.craft()
- self.assertEqual(result,None)
[docs]@override_settings(CRAFT_RECIPE_MODULES=[])
-classTestCraftingRecipe(BaseEvenniaTestCase):
- """
- Test the CraftingRecipe class with one recipe
- """
-
- maxDiff=None
-
-
[docs]deftest_craft__success(self):
- """Test to create a result from the recipe"""
- recipe=_MockRecipe(
- self.crafter,self.tool1,self.tool2,self.cons1,self.cons2,self.cons3
- )
-
- result=recipe.craft()
-
- self.assertEqual(result[0].key,"Result1")
- self.assertEqual(result[0].tags.all(),["result1","resultprot"])
- self.crafter.msg.assert_called_with(
- recipe.success_message.format(outputs="Result1"),{"type":"crafting"}
- )
-
- # make sure consumables are gone
- self.assertIsNone(self.cons1.pk)
- self.assertIsNone(self.cons2.pk)
- self.assertIsNone(self.cons3.pk)
- # make sure tools remain
- self.assertIsNotNone(self.tool1.pk)
- self.assertIsNotNone(self.tool2.pk)
-
-
[docs]deftest_seed__success(self):
- """Test seed helper classmethod"""
-
- # needed for other dbs to pass seed
- homeroom=create_object(key="HomeRoom",nohome=True)
-
- # call classmethod directly
- withoverride_settings(DEFAULT_HOME=f"#{homeroom.id}"):
- tools,consumables=_MockRecipe.seed()
-
- # this should be a normal successful crafting
- recipe=_MockRecipe(self.crafter,*(tools+consumables))
-
- result=recipe.craft()
-
- self.assertEqual(result[0].key,"Result1")
- self.assertEqual(result[0].tags.all(),["result1","resultprot"])
- self.crafter.msg.assert_called_with(
- recipe.success_message.format(outputs="Result1"),{"type":"crafting"}
- )
-
- # make sure consumables are gone
- forconsinconsumables:
- self.assertIsNone(cons.pk)
- # make sure tools remain
- fortoolintools:
- self.assertIsNotNone(tool.pk)
-
-
[docs]deftest_craft_missing_tool__fail(self):
- """Fail craft by missing tool2"""
- recipe=_MockRecipe(self.crafter,self.tool1,self.cons1,self.cons2,self.cons3)
- result=recipe.craft()
- self.assertFalse(result)
- self.crafter.msg.assert_called_with(
- recipe.error_tool_missing_message.format(outputs="Result1",missing="tool2"),
- {"type":"crafting"},
- )
-
- # make sure consumables are still there
- self.assertIsNotNone(self.cons1.pk)
- self.assertIsNotNone(self.cons2.pk)
- self.assertIsNotNone(self.cons3.pk)
- # make sure tools remain
- self.assertIsNotNone(self.tool1.pk)
- self.assertIsNotNone(self.tool2.pk)
-
-
[docs]deftest_craft_missing_cons__fail(self):
- """Fail craft by missing cons3"""
- recipe=_MockRecipe(self.crafter,self.tool1,self.tool2,self.cons1,self.cons2)
- result=recipe.craft()
- self.assertFalse(result)
- self.crafter.msg.assert_called_with(
- recipe.error_consumable_missing_message.format(outputs="Result1",missing="cons3"),
- {"type":"crafting"},
- )
-
- # make sure consumables are still there
- self.assertIsNotNone(self.cons1.pk)
- self.assertIsNotNone(self.cons2.pk)
- self.assertIsNotNone(self.cons3.pk)
- # make sure tools remain
- self.assertIsNotNone(self.tool1.pk)
- self.assertIsNotNone(self.tool2.pk)
-
-
[docs]deftest_craft_missing_cons__always_consume__fail(self):
- """Fail craft by missing cons3, with always-consume flag"""
-
- cons4=create_object(key="cons4",tags=[("cons4","crafting_material")],nohome=True)
-
- recipe=_MockRecipe(self.crafter,self.tool1,self.tool2,self.cons1,self.cons2,cons4)
- recipe.consume_on_fail=True
-
- result=recipe.craft()
-
- self.assertFalse(result)
- self.crafter.msg.assert_called_with(
- recipe.error_consumable_missing_message.format(outputs="Result1",missing="cons3"),
- {"type":"crafting"},
- )
-
- # make sure consumables are deleted even though we failed
- self.assertIsNone(self.cons1.pk)
- self.assertIsNone(self.cons2.pk)
- # the extra should also be gone
- self.assertIsNone(cons4.pk)
- # but cons3 should be fine since it was not included
- self.assertIsNotNone(self.cons3.pk)
- # make sure tools remain as normal
- self.assertIsNotNone(self.tool1.pk)
- self.assertIsNotNone(self.tool2.pk)
-
-
[docs]deftest_craft_wrong_tool__fail(self):
- """Fail craft by including a wrong tool"""
-
- wrong=create_object(key="wrong",tags=[("wrongtool","crafting_tool")],nohome=True)
-
- recipe=_MockRecipe(self.crafter,self.tool1,self.tool2,self.cons1,self.cons2,wrong)
- result=recipe.craft()
- self.assertFalse(result)
- self.crafter.msg.assert_called_with(
- recipe.error_tool_excess_message.format(
- outputs="Result1",excess=wrong.get_display_name(looker=self.crafter)
- ),
- {"type":"crafting"},
- )
- # make sure consumables are still there
- self.assertIsNotNone(self.cons1.pk)
- self.assertIsNotNone(self.cons2.pk)
- self.assertIsNotNone(self.cons3.pk)
- # make sure tools remain
- self.assertIsNotNone(self.tool1.pk)
- self.assertIsNotNone(self.tool2.pk)
-
-
[docs]deftest_craft_tool_excess__fail(self):
- """Fail by too many consumables"""
-
- # note that this is a valid tag!
- tool3=create_object(key="tool3",tags=[("tool2","crafting_tool")],nohome=True)
-
- recipe=_MockRecipe(
- self.crafter,self.tool1,self.tool2,self.cons1,self.cons2,self.cons3,tool3
- )
- result=recipe.craft()
- self.assertFalse(result)
- self.crafter.msg.assert_called_with(
- recipe.error_tool_excess_message.format(
- outputs="Result1",excess=tool3.get_display_name(looker=self.crafter)
- ),
- {"type":"crafting"},
- )
-
- # make sure consumables are still there
- self.assertIsNotNone(self.cons1.pk)
- self.assertIsNotNone(self.cons2.pk)
- self.assertIsNotNone(self.cons3.pk)
- # make sure tools remain
- self.assertIsNotNone(self.tool1.pk)
- self.assertIsNotNone(self.tool2.pk)
- self.assertIsNotNone(tool3.pk)
-
-
[docs]deftest_craft_cons_excess__fail(self):
- """Fail by too many consumables"""
-
- # note that this is a valid tag!
- cons4=create_object(key="cons4",tags=[("cons3","crafting_material")],nohome=True)
-
- recipe=_MockRecipe(
- self.crafter,self.tool1,self.tool2,self.cons1,self.cons2,self.cons3,cons4
- )
- result=recipe.craft()
- self.assertFalse(result)
- self.crafter.msg.assert_called_with(
- recipe.error_consumable_excess_message.format(
- outputs="Result1",excess=cons4.get_display_name(looker=self.crafter)
- ),
- {"type":"crafting"},
- )
-
- # make sure consumables are still there
- self.assertIsNotNone(self.cons1.pk)
- self.assertIsNotNone(self.cons2.pk)
- self.assertIsNotNone(self.cons3.pk)
- self.assertIsNotNone(cons4.pk)
- # make sure tools remain
- self.assertIsNotNone(self.tool1.pk)
- self.assertIsNotNone(self.tool2.pk)
[docs]deftest_setgender(self):
- self.call(gendersub.SetGender(),"male","Your gender was set to male.")
- self.call(gendersub.SetGender(),"ambiguous","Your gender was set to ambiguous.")
- self.call(gendersub.SetGender(),"Foo","Usage: @gender")
[docs]deftest_mail(self):
- self.call(mail.CmdMail(),"2","'2' is not a valid mail id.",caller=self.account)
- self.call(mail.CmdMail(),"test","'test' is not a valid mail id.",caller=self.account)
- self.call(mail.CmdMail(),"","There are no messages in your inbox.",caller=self.account)
- self.call(
- mail.CmdMailCharacter(),
- "Char=Message 1",
- "You have received a new @mail from Char|You sent your message.",
- caller=self.char1,
- )
- self.call(
- mail.CmdMailCharacter(),"Char=Message 2","You sent your message.",caller=self.char2
- )
- self.call(
- mail.CmdMail(),
- "TestAccount2=Message 2",
- "You have received a new @mail from TestAccount2",
- caller=self.account2,
- )
- self.call(
- mail.CmdMail(),"TestAccount=Message 1","You sent your message.",caller=self.account2
- )
- self.call(
- mail.CmdMail(),"TestAccount=Message 2","You sent your message.",caller=self.account2
- )
- self.call(mail.CmdMail(),"","| ID From Subject",caller=self.account)
- self.call(mail.CmdMail(),"2","From: TestAccount2",caller=self.account)
- self.call(
- mail.CmdMail(),
- "/forward TestAccount2 = 1/Forward message",
- "You sent your message.|Message forwarded.",
- caller=self.account,
- )
- self.call(
- mail.CmdMail(),"/reply 2=Reply Message2","You sent your message.",caller=self.account
- )
- self.call(mail.CmdMail(),"/delete 2","Message 2 deleted",caller=self.account)
[docs]deftest_cmd_use(self):
-
- self._use("","Use what?")
- self._use("something","There is no something around.")
- self._use("steel","You have no idea how this can be used")
- self._use("steel flint","There is no steel flint around.")
- self._use("steel, flint","You have no idea how these can be used")
-
- recipe_dbref=self._good_recipe(
- "makefire",["steel","flint"],["fire"],and_destroy_it=False
- )
- recipe2_dbref=self._good_recipe(
- "makefire2",["steel","flint"],["fire"],and_destroy_it=False,expected_count=2
- )
-
- # although there is steel and flint
- # those aren't valid puzzle parts because
- # the puzzle hasn't been armed
- self._use("steel","You have no idea how this can be used")
- self._use("steel, flint","You have no idea how these can be used")
- self._arm(recipe_dbref,"makefire",["steel","flint"])
- self._check_room_contents({"steel":2,"flint":2},check_test_tags=True)
-
- # there are duplicated objects now
- self._use("steel","Which steel. There are many")
- self._use("flint","Which flint. There are many")
-
- # delete proto parts and proto results
- self.steel.delete()
- self.flint.delete()
- self.fire.delete()
-
- # solve puzzle
- self._use("steel, flint","You are a Genius")
- self.assertEqual(
- 1,
- len(
- list(
- filter(
- lambdao:o.key=="fire"
- and("makefire",puzzles._PUZZLES_TAG_CATEGORY)
- ino.tags.all(return_key_and_category=True)
- and(puzzles._PUZZLES_TAG_MEMBER,puzzles._PUZZLES_TAG_CATEGORY)
- ino.tags.all(return_key_and_category=True),
- self.room1.contents,
- )
- )
- ),
- )
- self._check_room_contents({"steel":0,"flint":0,"fire":1},check_test_tags=True)
-
- # trying again will fail as it was resolved already
- # and the parts were destroyed
- self._use("steel, flint","There is no steel around")
- self._use("flint, steel","There is no flint around")
-
- # arm same puzzle twice so there are duplicated parts
- self._arm(recipe_dbref,"makefire",["steel","flint"])
- self._arm(recipe_dbref,"makefire",["steel","flint"])
- self._check_room_contents({"steel":2,"flint":2,"fire":1},check_test_tags=True)
-
- # try solving with multiple parts but incomplete set
- self._use(
- "steel-1, steel-2","You try to utilize these but nothing happens ... something amiss?"
- )
-
- # arm the other puzzle. Their parts are identical
- self._arm(recipe2_dbref,"makefire2",["steel","flint"])
- self._check_room_contents({"steel":3,"flint":3,"fire":1},check_test_tags=True)
-
- # solve with multiple parts for
- # multiple puzzles. Both can be solved but
- # only one is.
- self._use(
- "steel-1, flint-2, steel-3, flint-3",
- "Your gears start turning and 2 different ideas come to your mind ... ",
- )
- self._check_room_contents({"steel":2,"flint":2,"fire":2},check_test_tags=True)
-
- self.room1.msg_contents=Mock()
-
- # solve all
- self._use("steel-1, flint-1","You are a Genius")
- self.room1.msg_contents.assert_called_once_with(
- "|cChar|n performs some kind of tribal dance and |yfire|n seems to appear from thin air",
- exclude=(self.char1,),
- )
- self._use("steel, flint","You are a Genius")
- self._check_room_contents({"steel":0,"flint":0,"fire":4},check_test_tags=True)
-
-
[docs]deftest_puzzleedit(self):
- recipe_dbref=self._good_recipe(
- "makefire",["steel","flint"],["fire"],and_destroy_it=False
- )
-
- def_puzzleedit(swt,dbref,args,expmsg):
- if(swtisNone)and(dbrefisNone)and(argsisNone):
- cmdstr=""
- else:
- cmdstr="%s%s%s"%(swt,dbref,args)
- self.call(puzzles.CmdEditPuzzle(),cmdstr,expmsg,caller=self.char1)
-
- # delete proto parts and proto results
- self.steel.delete()
- self.flint.delete()
- self.fire.delete()
-
- sid=self.script.id
- # bad syntax
- _puzzleedit(
- None,None,None,"A puzzle recipe's #dbref must be specified.\nUsage: @puzzleedit"
- )
- _puzzleedit("","1","","A puzzle recipe's #dbref must be specified.\nUsage: @puzzleedit")
- _puzzleedit("","","","A puzzle recipe's #dbref must be specified.\nUsage: @puzzleedit")
- _puzzleedit(
- "",
- recipe_dbref,
- "dummy",
- "A puzzle recipe's #dbref must be specified.\nUsage: @puzzleedit",
- )
- _puzzleedit("",self.script.dbref,"","Script(#{}) is not a puzzle".format(sid))
-
- # edit use_success_message and use_success_location_message
- _puzzleedit(
- "",
- recipe_dbref,
- "/use_success_message = Yes!",
- "makefire(%s) use_success_message = Yes!"%recipe_dbref,
- )
- _puzzleedit(
- "",
- recipe_dbref,
- "/use_success_location_message = {result_names} Yeah baby! {caller}",
- "makefire(%s) use_success_location_message = {result_names} Yeah baby! {caller}"
- %recipe_dbref,
- )
-
- self._arm(recipe_dbref,"makefire",["steel","flint"])
- self.room1.msg_contents=Mock()
- self._use("steel, flint","Yes!")
- self.room1.msg_contents.assert_called_once_with(
- "fire Yeah baby! Char",exclude=(self.char1,)
- )
- self.room1.msg_contents.reset_mock()
-
- # edit mask: exclude location and desc during matching
- _puzzleedit(
- "",
- recipe_dbref,
- "/mask = location,desc",
- "makefire(%s) mask = ('location', 'desc')"%recipe_dbref,
- )
-
- self._arm(recipe_dbref,"makefire",["steel","flint"])
- # change location and desc
- self.char1.search("steel").db.desc="A solid bar of steel"
- self.char1.search("steel").location=self.char1
- self.char1.search("flint").db.desc="A flint steel"
- self.char1.search("flint").location=self.char1
- self._use("steel, flint","Yes!")
- self.room1.msg_contents.assert_called_once_with(
- "fire Yeah baby! Char",exclude=(self.char1,)
- )
-
- # delete
- _puzzleedit("/delete",recipe_dbref,"","makefire(%s) was deleted"%recipe_dbref)
- self._assert_no_recipes()
-
-
[docs]deftest_puzzleedit_add_remove_parts_results(self):
- recipe_dbref=self._good_recipe(
- "makefire",["steel","flint"],["fire"],and_destroy_it=False
- )
-
- def_puzzleedit(swt,dbref,rhslist,expmsg):
- cmdstr="%s%s = %s"%(swt,dbref,", ".join(rhslist))
- self.call(puzzles.CmdEditPuzzle(),cmdstr,expmsg,caller=self.char1)
-
- red_steel=create_object(
- self.object_typeclass,key="red steel",location=self.char1.location
- )
- smoke=create_object(self.object_typeclass,key="smoke",location=self.char1.location)
-
- _puzzleedit("/addresult",recipe_dbref,["smoke"],"smoke were added to results")
- _puzzleedit(
- "/addpart",recipe_dbref,["red steel","steel"],"red steel, steel were added to parts"
- )
-
- # create a box so we can put all objects in
- # so that they can't be found during puzzle resolution
- self.box=create_object(self.object_typeclass,key="box",location=self.char1.location)
-
- def_box_all():
- foroinself.room1.contents:
- ifonotin[self.char1,self.char2,self.exit,self.obj1,self.obj2,self.box]:
- o.location=self.box
-
- _box_all()
-
- self._arm(recipe_dbref,"makefire",["steel","flint","red steel","steel"])
- self._check_room_contents({"steel":2,"red steel":1,"flint":1})
- self._use(
- "steel-1, flint","You try to utilize these but nothing happens ... something amiss?"
- )
- self._use("steel-1, flint, red steel, steel-2","You are a Genius")
- self._check_room_contents({"smoke":1,"fire":1})
- _box_all()
-
- self.fire.location=self.room1
- self.steel.location=self.room1
-
- _puzzleedit("/delresult",recipe_dbref,["fire"],"fire were removed from results")
- _puzzleedit(
- "/delpart",recipe_dbref,["steel","steel"],"steel, steel were removed from parts"
- )
-
- _box_all()
-
- self._arm(recipe_dbref,"makefire",["flint","red steel"])
- self._check_room_contents({"red steel":1,"flint":1})
- self._use("red steel, flint","You are a Genius")
- self._check_room_contents({"smoke":1,"fire":0})
[docs]classTestTurnBattleBasicCmd(BaseEvenniaCommandTest):
-
- # Test basic combat commands
-
[docs]deftest_turnbattlecmd(self):
- self.call(tb_basic.CmdFight(),"","You can't start a fight if you've been defeated!")
- self.call(tb_basic.CmdAttack(),"","You can only do that in combat. (see: help fight)")
- self.call(tb_basic.CmdPass(),"","You can only do that in combat. (see: help fight)")
- self.call(tb_basic.CmdDisengage(),"","You can only do that in combat. (see: help fight)")
- self.call(tb_basic.CmdRest(),"","Char rests to recover HP.")
[docs]deftest_turnbattleequipcmd(self):
- # Start with equip module specific commands.
- self.call(tb_equip.CmdWield(),"weapon","Char wields test weapon.")
- self.call(tb_equip.CmdUnwield(),"","Char lowers test weapon.")
- self.call(tb_equip.CmdDon(),"armor","Char dons test armor.")
- self.call(tb_equip.CmdDoff(),"","Char removes test armor.")
- # Also test the commands that are the same in the basic module
- self.call(tb_equip.CmdFight(),"","You can't start a fight if you've been defeated!")
- self.call(tb_equip.CmdAttack(),"","You can only do that in combat. (see: help fight)")
- self.call(tb_equip.CmdPass(),"","You can only do that in combat. (see: help fight)")
- self.call(tb_equip.CmdDisengage(),"","You can only do that in combat. (see: help fight)")
- self.call(tb_equip.CmdRest(),"","Char rests to recover HP.")
-
-
-
[docs]classTestTurnBattleRangeCmd(BaseEvenniaCommandTest):
- # Test range commands
-
[docs]deftest_turnbattlerangecmd(self):
- # Start with range module specific commands.
- self.call(tb_range.CmdShoot(),"","You can only do that in combat. (see: help fight)")
- self.call(tb_range.CmdApproach(),"","You can only do that in combat. (see: help fight)")
- self.call(tb_range.CmdWithdraw(),"","You can only do that in combat. (see: help fight)")
- self.call(tb_range.CmdStatus(),"","HP Remaining: 100 / 100")
- # Also test the commands that are the same in the basic module
- self.call(tb_range.CmdFight(),"","There's nobody here to fight!")
- self.call(tb_range.CmdAttack(),"","You can only do that in combat. (see: help fight)")
- self.call(tb_range.CmdPass(),"","You can only do that in combat. (see: help fight)")
- self.call(tb_range.CmdDisengage(),"","You can only do that in combat. (see: help fight)")
- self.call(tb_range.CmdRest(),"","Char rests to recover HP.")
[docs]deftest_turnbattleitemcmd(self):
- self.call(tb_items.CmdUse(),"item","'Test item' is not a usable item.")
- # Also test the commands that are the same in the basic module
- self.call(tb_items.CmdFight(),"","You can't start a fight if you've been defeated!")
- self.call(tb_items.CmdAttack(),"","You can only do that in combat. (see: help fight)")
- self.call(tb_items.CmdPass(),"","You can only do that in combat. (see: help fight)")
- self.call(tb_items.CmdDisengage(),"","You can only do that in combat. (see: help fight)")
- self.call(tb_items.CmdRest(),"","Char rests to recover HP.")
-
-
-
[docs]classTestTurnBattleMagicCmd(BaseEvenniaCommandTest):
-
- # Test magic commands
-
[docs]deftest_turnbattlemagiccmd(self):
- self.call(tb_magic.CmdStatus(),"","You have 100 / 100 HP and 20 / 20 MP.")
- self.call(tb_magic.CmdLearnSpell(),"test spell","There is no spell with that name.")
- self.call(tb_magic.CmdCast(),"","Usage: cast <spell name> = <target>, <target2>")
- # Also test the commands that are the same in the basic module
- self.call(tb_magic.CmdFight(),"","There's nobody here to fight!")
- self.call(tb_magic.CmdAttack(),"","You can only do that in combat. (see: help fight)")
- self.call(tb_magic.CmdPass(),"","You can only do that in combat. (see: help fight)")
- self.call(tb_magic.CmdDisengage(),"","You can only do that in combat. (see: help fight)")
- self.call(tb_magic.CmdRest(),"","Char rests to recover HP and MP.")
[docs]classForceUTCDatetime(datetime.datetime):
-
- """Force UTC datetime."""
-
-
[docs]@classmethod
- deffromtimestamp(cls,timestamp):
- """Force fromtimestamp to run with naive datetimes."""
- returndatetime.datetime.utcfromtimestamp(timestamp)
-
-
-
[docs]@patch("evennia.contrib.grid.extended_room.extended_room.datetime.datetime",ForceUTCDatetime)
-# mock gametime to return April 9, 2064, at 21:06 (spring evening)
-@patch("evennia.utils.gametime.gametime",new=Mock(return_value=2975000766))
-classTestExtendedRoom(BaseEvenniaCommandTest):
- room_typeclass=extended_room.ExtendedRoom
- DETAIL_DESC="A test detail."
- SPRING_DESC="A spring description."
- OLD_DESC="Old description."
- settings.TIME_ZONE="UTC"
-
-
[docs]deftest_return_appearance(self):
- # get the appearance of a non-extended room for contrast purposes
- old_desc=DefaultRoom.return_appearance(self.room1,self.char1)
- # the new appearance should be the old one, but with the desc switched
- self.assertEqual(
- old_desc.replace(self.OLD_DESC,self.SPRING_DESC),
- self.room1.return_appearance(self.char1),
- )
- self.assertEqual("spring",self.room1.ndb.last_season)
- self.assertEqual("evening",self.room1.ndb.last_timeslot)
[docs]deftest_wilderness_correct_exits(self):
- wilderness.create_wilderness()
- wilderness.enter_wilderness(self.char1)
-
- # By default we enter at a corner (0, 0), so only a few exits should
- # be visible / traversable
- exits=[
- i
- foriinself.char1.location.contents
- ifi.destinationand(i.access(self.char1,"view")ori.access(self.char1,"traverse"))
- ]
-
- self.assertEqual(len(exits),3)
- exitsok=["north","northeast","east"]
- foreach_exitinexitsok:
- self.assertTrue(any([eforeinexitsife.key==each_exit]))
-
- # If we move to another location not on an edge, then all directions
- # should be visible / traversable
- wilderness.enter_wilderness(self.char1,coordinates=(1,1))
- exits=[
- i
- foriinself.char1.location.contents
- ifi.destinationand(i.access(self.char1,"view")ori.access(self.char1,"traverse"))
- ]
- self.assertEqual(len(exits),8)
- exitsok=[
- "north",
- "northeast",
- "east",
- "southeast",
- "south",
- "southwest",
- "west",
- "northwest",
- ]
- foreach_exitinexitsok:
- self.assertTrue(any([eforeinexitsife.key==each_exit]))
-
-
[docs]deftest_room_creation(self):
- # Pretend that both char1 and char2 are connected...
- self.char1.sessions.add(1)
- self.char2.sessions.add(1)
- self.assertTrue(self.char1.has_account)
- self.assertTrue(self.char2.has_account)
-
- wilderness.create_wilderness()
- w=self.get_wilderness_script()
-
- # We should have no unused room after moving the first account in.
- self.assertEqual(len(w.db.unused_rooms),0)
- w.move_obj(self.char1,(0,0))
- self.assertEqual(len(w.db.unused_rooms),0)
-
- # And also no unused room after moving the second one in.
- w.move_obj(self.char2,(1,1))
- self.assertEqual(len(w.db.unused_rooms),0)
-
- # But if char2 moves into char1's room, we should have one unused room
- # Which should be char2's old room that got created.
- w.move_obj(self.char2,(0,0))
- self.assertEqual(len(w.db.unused_rooms),1)
- self.assertEqual(self.char1.location,self.char2.location)
-
- # And if char2 moves back out, that unused room should be put back to
- # use again.
- w.move_obj(self.char2,(1,1))
- self.assertNotEqual(self.char1.location,self.char2.location)
- self.assertEqual(len(w.db.unused_rooms),0)
-
- @parameterized.expand(
- [
- ((0,0),"| \n#-",[["|"," "],["#","-"]]),
- ((1,0)," |\n-#",[[" ","|"],["-","#"]]),
- ((0,1),"#-\n| ",[["#","-"],["|"," "]]),
- ((1,1),"-#\n |",[["-","#"],[" ","|"]]),
- ]
- )
- deftest_get_visual_range__scan(self,coord,expectstr,expectlst):
- """
- Test displaying a part of the map around a central point.
-
- """
- mapstr=self.map.get_visual_range(coord,dist=1,mode="scan",character=None)
- maplst=self.map.get_visual_range(
- coord,dist=1,mode="scan",return_str=False,character=None
- )
- maplst=[[part.replace("||","|")forpartinpartlst]forpartlstinmaplst]
- self.assertEqual(expectstr,mapstr.replace("||","|"))
- self.assertEqual(expectlst,maplst[::-1])
-
- @parameterized.expand(
- [
- ((0,0),"| \n@-",[["|"," "],["@","-"]]),
- ((1,0)," |\n-@",[[" ","|"],["-","@"]]),
- ((0,1),"@-\n| ",[["@","-"],["|"," "]]),
- ((1,1),"-@\n |",[["-","@"],[" ","|"]]),
- ]
- )
- deftest_get_visual_range__scan__character(self,coord,expectstr,expectlst):
- """
- Test displaying a part of the map around a central point, showing the
- character @-symbol in that spot.
-
- """
- mapstr=self.map.get_visual_range(coord,dist=1,mode="scan",character="@")
- maplst=self.map.get_visual_range(
- coord,dist=1,mode="scan",return_str=False,character="@"
- )
- maplst=[[part.replace("||","|")forpartinpartlst]forpartlstinmaplst]
- self.assertEqual(expectstr,mapstr.replace("||","|"))
- self.assertEqual(expectlst,maplst[::-1])# flip y-axis for print
-
- @parameterized.expand(
- [
- ((0,0),1,"# \n| \n@-#"),
- ((0,1),1,"@-#\n| \n# "),
- ((1,0),1," #\n |\n#-@"),
- ((1,1),1,"#-@\n |\n #"),
- ((0,0),2,"#-#\n| |\n@-#"),
- ]
- )
- deftest_get_visual_range__nodes__character(self,coord,dist,expected):
- """
- Get sub-part of map with node-mode.
-
- """
- mapstr=self.map.get_visual_range(coord,dist=dist,mode="nodes",character="@")
- self.assertEqual(expected,mapstr.replace("||","|"))
-
-
[docs]deftest_spawn(self):
- """
- Spawn the map into actual objects.
-
- """
- self.grid.spawn()
- self.assertEqual(xyzroom.XYZRoom.objects.all().count(),4)
- self.assertEqual(xyzroom.XYZExit.objects.all().count(),8)
-
-
-
[docs]classTestMap2(_MapTest):
- """
- Test with Map2 - a bigger map with multi-step links
-
- """
-
- map_data={"map":MAP2,"zcoord":"map2"}
- map_display=MAP2_DISPLAY
-
-
[docs]deftest_str_output(self):
- """Check the display_map"""
- # strip the leftover spaces on the right to better
- # work with text editor stripping this automatically ...
- stripped_map="\n".join(line.rstrip()forlineinstr(self.map).split("\n"))
- self.assertEqual(stripped_map.replace("||","|"),MAP2_DISPLAY)
[docs]deftest_spawn(self):
- """
- Spawn the map into actual objects.
-
- """
- self.grid.spawn()
- self.assertEqual(xyzroom.XYZRoom.objects.all().count(),16)
- self.assertEqual(xyzroom.XYZExit.objects.all().count(),44)
-
-
-
[docs]classTestMap5(_MapTest):
- """
- Test Map5 - Small map with one-way links
-
- """
-
- map_data={"map":MAP5,"zcoord":"map5"}
- map_display=MAP5_DISPLAY
-
-
[docs]deftest_str_output(self):
- """Check the display_map"""
- stripped_map="\n".join(line.rstrip()forlineinstr(self.map).split("\n"))
- self.assertEqual(MAP5_DISPLAY,stripped_map.replace("||","|"))
-
- @parameterized.expand(
- [
- ((0,0),(1,0),("e",)),# cross one-way
- ((1,0),(0,0),()),# blocked
- ((0,1),(1,1),("e",)),# should still take shortest
- ((1,1),(0,1),("n","w","s")),# take long way around
- ]
- )
- deftest_shortest_path(self,startcoord,endcoord,expected_directions):
- """
- Test shortest-path calculations throughout the grid.
-
- """
- directions,_=self.map.get_shortest_path(startcoord,endcoord)
- self.assertEqual(expected_directions,tuple(directions))
-
-
[docs]deftest_spawn(self):
- """
- Spawn the map into actual objects.
-
- """
- self.grid.spawn()
- self.assertEqual(xyzroom.XYZRoom.objects.all().count(),6)
- self.assertEqual(xyzroom.XYZExit.objects.all().count(),8)
-
-
-
[docs]classTestMap6(_MapTest):
- """
- Test Map6 - Bigger map with one-way links in different directions
-
- """
-
- map_data={"map":MAP6,"zcoord":"map6"}
- map_display=MAP6_DISPLAY
-
-
[docs]deftest_str_output(self):
- """Check the display_map"""
- stripped_map="\n".join(line.rstrip()forlineinstr(self.map).split("\n"))
- self.assertEqual(MAP6_DISPLAY,stripped_map.replace("||","|"))
-
- @parameterized.expand(
- [
- ((0,0),(2,0),("e","e")),# cross one-way
- ((2,0),(0,0),("e","n","w","s","w")),# blocked, long way around
- ((4,0),(3,0),("w",)),
- ((3,0),(4,0),("n","e","s")),
- ((1,1),(1,2),("n",)),
- ((1,2),(1,1),("e","e","s","w")),
- ((3,1),(1,4),("w","n","n")),
- ((0,4),(0,0),("e","e","e","s","s","s","w","s","w")),
- ]
- )
- deftest_shortest_path(self,startcoord,endcoord,expected_directions):
- """
- Test shortest-path calculations throughout the grid.
-
- """
- directions,_=self.map.get_shortest_path(startcoord,endcoord)
- self.assertEqual(expected_directions,tuple(directions))
-
-
[docs]deftest_spawn(self):
- """
- Spawn the map into actual objects.
-
- """
- self.grid.spawn()
- self.assertEqual(xyzroom.XYZRoom.objects.all().count(),18)
- self.assertEqual(xyzroom.XYZExit.objects.all().count(),38)
-
-
-
[docs]classTestMap7(_MapTest):
- """
- Test Map7 - Small test of dynamic link node
-
- """
-
- map_data={"map":MAP7,"zcoord":"map7"}
- map_display=MAP7_DISPLAY
-
-
[docs]deftest_str_output(self):
- """Check the display_map"""
- stripped_map="\n".join(line.rstrip()forlineinstr(self.map).split("\n"))
- self.assertEqual(MAP7_DISPLAY,stripped_map.replace("||","|"))
[docs]deftest_spawn(self):
- """
- Spawn the map into actual objects.
-
- """
- self.grid.spawn()
- self.assertEqual(xyzroom.XYZRoom.objects.all().count(),6)
- self.assertEqual(xyzroom.XYZExit.objects.all().count(),8)
-
-
-
[docs]classTestMap8(_MapTest):
- """
- Test Map8 - Small test of dynamic link node
-
- """
-
- map_data={"map":MAP8,"zcoord":"map8"}
- map_display=MAP8_DISPLAY
-
-
[docs]deftest_str_output(self):
- """Check the display_map"""
- stripped_map="\n".join(line.rstrip()forlineinstr(self.map).split("\n"))
- self.assertEqual(MAP8_DISPLAY,stripped_map.replace("||","|"))
[docs]deftest_spawn(self):
- """
- Spawn the map into actual objects.
-
- """
- self.grid.spawn()
- self.assertEqual(xyzroom.XYZRoom.objects.all().count(),12)
- self.assertEqual(xyzroom.XYZExit.objects.all().count(),24)
-
-
-
[docs]classTestMap10(_MapTest):
- """
- Test Map10 - a map with blocked- and interrupt links/nodes. These are
- 'invisible' nodes and won't show up in the map display.
-
- """
-
- map_data={"map":MAP10,"zcoord":"map10"}
- map_display=MAP10_DISPLAY
-
-
[docs]deftest_str_output(self):
- """Check the display_map"""
- stripped_map="\n".join(line.rstrip()forlineinstr(self.map).split("\n"))
- self.assertEqual(MAP10_DISPLAY,stripped_map.replace("||","|"))
[docs]@patch("evennia.contrib.rpg.buffs.buff.utils.delay",new=Mock())
- deftest_details(self):
- """tests that buff details like name and flavor are correct; also test modifier viewing"""
- handler:BuffHandler=self.testobj.buffs
- handler.add(_TestModBuff)
- handler.add(_TestTrigBuff)
- self.assertEqual(handler.get("tmb").flavor,"modderbuff")
- self.assertEqual(handler.get("ttb").name,"ttb")
- mods=handler.view_modifiers("stat1")
- _testmods={
- "add":{"total":15,"strongest":15},
- "mult":{"total":0,"strongest":0},
- "div":{"total":0,"strongest":0},
- }
- self.assertDictEqual(mods,_testmods)
-
-
[docs]@patch("evennia.contrib.rpg.buffs.buff.utils.delay",new=Mock())
- deftest_modify(self):
- """tests to ensure that values are modified correctly, and stack across mods"""
- # setup
- handler:BuffHandler=self.testobj.buffs
- _stat1,_stat2=0,10
- handler.add(_TestModBuff)
- # stat1 and 2 basic mods
- self.assertEqual(handler.check(_stat1,"stat1"),15)
- self.assertEqual(handler.check(_stat2,"stat2"),15)
- # checks can take any base value
- self.assertEqual(handler.check(_stat1,"stat2"),0)
- self.assertEqual(handler.check(_stat2,"stat1"),25)
- # change to base stat reflected in check
- _stat1+=5
- self.assertEqual(handler.check(_stat1,"stat1"),20)
- _stat2+=10
- self.assertEqual(handler.check(_stat2,"stat2"),30)
- # test stacking; single stack, multiple stack, max stacks
- handler.add(_TestModBuff)
- self.assertEqual(handler.check(_stat1,"stat1"),25)
- handler.add(_TestModBuff,stacks=3)
- self.assertEqual(handler.check(_stat1,"stat1"),40)
- handler.add(_TestModBuff,stacks=5)
- self.assertEqual(handler.check(_stat1,"stat1"),40)
- # stat2 mod doesn't stack
- self.assertEqual(handler.check(_stat2,"stat2"),30)
- # layers with second mod
- handler.add(_TestModBuff2)
- self.assertEqual(handler.check(_stat1,"stat1"),100)
- self.assertEqual(handler.check(_stat2,"stat2"),30)
- # apply only the strongest value
- self.assertEqual(handler.check(_stat1,"stat1",strongest=True),80)
- # removing mod properly reduces value, doesn't affect other mods
- handler.remove_by_type(_TestModBuff)
- self.assertEqual(handler.check(_stat1,"stat1"),30)
- self.assertEqual(handler.check(_stat2,"stat2"),20)
- # divider mod test
- handler.add(_TestDivBuff)
- self.assertEqual(handler.check(_stat1,"stat1"),15)
[docs]@patch("evennia.contrib.rpg.buffs.buff.utils.delay",new=Mock())
- deftest_cacheattrlink(self):
- """tests the link between the instance attribute and the cache attribute"""
- # setup
- handler:BuffHandler=self.testobj.buffs
- handler.add(_EmptyBuff)
- self.assertEqual(handler.buffcache["empty"]["duration"],-1)
- empty:_EmptyBuff=handler.get("empty")
- empty.duration=30
- self.assertEqual(handler.buffcache["empty"]["duration"],30)
[docs]deftest_cmddice(self,mocked_randint):
- self.call(
- dice.CmdDice(),"3d6 + 4","You roll 3d6 + 4.| Roll(s): 5, 5 and 5. Total result is 19."
- )
- self.call(dice.CmdDice(),"100000d1000","The maximum roll allowed is 10000d10000.")
- self.call(dice.CmdDice(),"/secret 3d6 + 4","You roll 3d6 + 4 (secret, not echoed).")
Source code for evennia.contrib.rpg.rpsystem.tests
-"""
-Tests for RP system
-
-"""
-importtime
-
-fromanythingimportAnything
-
-fromevenniaimportcreate_object
-fromevennia.commands.default.testsimportBaseEvenniaCommandTest
-fromevennia.utils.test_resourcesimportBaseEvenniaTest
-
-from.importrplanguage,rpsystem
-
-mtrans={"testing":"1","is":"2","a":"3","human":"4"}
-atrans=["An","automated","advantageous","repeatable","faster"]
-
-text=(
- "Automated testing is advantageous for a number of reasons: "
- "tests may be executed Continuously without the need for human "
- "intervention, They are easily repeatable, and often faster."
-)
-
-
-
-
-
-# Testing of emoting / sdesc / recog system
-
-
-sdesc0="A nice sender of emotes"
-sdesc1="The first receiver of emotes."
-sdesc2="Another nice colliding sdesc-guy for tests"
-recog01="Mr Receiver"
-recog02="Mr Receiver2"
-recog10="Mr Sender"
-emote='With a flair, /me looks at /first and /colliding sdesc-guy. She says "This is a test."'
-fallback_emote="/Me is distracted from /first by /nomatch."
-case_emote="/Me looks at /first. Then, /me looks at /FIRST, /First and /Colliding twice."
-poss_emote="/Me frowns at /first for trying to steal /me's test."
-
-
-
[docs]deftest_parse_language(self):
- self.assertEqual(
- rpsystem.parse_language(self.speaker,emote),
- (
- "With a flair, /me looks at /first and /colliding sdesc-guy. She says {##0}",
- {"##0":(None,'"This is a test."')},
- ),
- )
[docs]deftest_get_sdesc(self):
- looker=self.speaker# Sender
- target=self.receiver1# Receiver1
- looker.sdesc.add(sdesc0)# A nice sender of emotes
- target.sdesc.add(sdesc1)# The first receiver of emotes.
-
- # sdesc with no processing
- self.assertEqual(looker.get_sdesc(target),"The first receiver of emotes.")
- # sdesc with processing
- self.assertEqual(
- looker.get_sdesc(target,process=True),"|bThe first receiver of emotes.|n"
- )
-
- looker.recog.add(target,recog01)# Mr Receiver
-
- # recog with no processing
- self.assertEqual(looker.get_sdesc(target),"Mr Receiver")
- # recog with processing
- self.assertEqual(looker.get_sdesc(target,process=True),"|mMr Receiver|n")
-
-
[docs]deftest_send_emote(self):
- speaker=self.speaker
- receiver1=self.receiver1
- receiver2=self.receiver2
- receivers=[speaker,receiver1,receiver2]
- speaker.sdesc.add(sdesc0)
- receiver1.sdesc.add(sdesc1)
- receiver2.sdesc.add(sdesc2)
- speaker.msg=lambdatext,**kwargs:setattr(self,"out0",text)
- receiver1.msg=lambdatext,**kwargs:setattr(self,"out1",text)
- receiver2.msg=lambdatext,**kwargs:setattr(self,"out2",text)
- rpsystem.send_emote(speaker,receivers,emote,case_sensitive=False)
- self.assertEqual(
- self.out0[0],
- "With a flair, |mSender|n looks at |bThe first receiver of emotes.|n "
- 'and |bAnother nice colliding sdesc-guy for tests|n. She says |w"This is a test."|n',
- )
- self.assertEqual(
- self.out1[0],
- "With a flair, |bA nice sender of emotes|n looks at |mReceiver1|n and "
- '|bAnother nice colliding sdesc-guy for tests|n. She says |w"This is a test."|n',
- )
- self.assertEqual(
- self.out2[0],
- "With a flair, |bA nice sender of emotes|n looks at |bThe first "
- 'receiver of emotes.|n and |mReceiver2|n. She says |w"This is a test."|n',
- )
-
-
[docs]deftest_send_emote_fallback(self):
- speaker=self.speaker
- receiver1=self.receiver1
- receiver2=self.receiver2
- receivers=[speaker,receiver1,receiver2]
- speaker.sdesc.add(sdesc0)
- receiver1.sdesc.add(sdesc1)
- receiver2.sdesc.add(sdesc2)
- speaker.msg=lambdatext,**kwargs:setattr(self,"out0",text)
- receiver1.msg=lambdatext,**kwargs:setattr(self,"out1",text)
- receiver2.msg=lambdatext,**kwargs:setattr(self,"out2",text)
- rpsystem.send_emote(speaker,receivers,fallback_emote,fallback="something")
- self.assertEqual(
- self.out0[0],
- "|mSender|n is distracted from |bthe first receiver of emotes.|n by something.",
- )
- self.assertEqual(
- self.out1[0],
- "|bA nice sender of emotes|n is distracted from |mReceiver1|n by something.",
- )
- self.assertEqual(
- self.out2[0],
- "|bA nice sender of emotes|n is distracted from |bthe first receiver of emotes.|n by something.",
- )
-
-
[docs]deftest_send_case_sensitive_emote(self):
- """Test new case-sensitive rp-parsing"""
- speaker=self.speaker
- receiver1=self.receiver1
- receiver2=self.receiver2
- receivers=[speaker,receiver1,receiver2]
- speaker.sdesc.add(sdesc0)
- receiver1.sdesc.add(sdesc1)
- receiver2.sdesc.add(sdesc2)
- speaker.msg=lambdatext,**kwargs:setattr(self,"out0",text)
- receiver1.msg=lambdatext,**kwargs:setattr(self,"out1",text)
- receiver2.msg=lambdatext,**kwargs:setattr(self,"out2",text)
- rpsystem.send_emote(speaker,receivers,case_emote)
- self.assertEqual(
- self.out0[0],
- "|mSender|n looks at |bthe first receiver of emotes.|n. Then, |mSender|n "
- "looks at |bTHE FIRST RECEIVER OF EMOTES.|n, |bThe first receiver of emotes.|n "
- "and |bAnother nice colliding sdesc-guy for tests|n twice.",
- )
- self.assertEqual(
- self.out1[0],
- "|bA nice sender of emotes|n looks at |mReceiver1|n. Then, "
- "|ba nice sender of emotes|n looks at |mReceiver1|n, |mReceiver1|n "
- "and |bAnother nice colliding sdesc-guy for tests|n twice.",
- )
- self.assertEqual(
- self.out2[0],
- "|bA nice sender of emotes|n looks at |bthe first receiver of emotes.|n. "
- "Then, |ba nice sender of emotes|n looks at |bTHE FIRST RECEIVER OF EMOTES.|n, "
- "|bThe first receiver of emotes.|n and |mReceiver2|n twice.",
- )
[docs]deftest_commands(self):
-
- self.call(
- rpsystem.CmdSdesc(),"Foobar Character","Char's sdesc was set to 'Foobar Character'."
- )
- self.call(
- rpsystem.CmdSdesc(),
- "BarFoo Character",
- "Char2's sdesc was set to 'BarFoo Character'.",
- caller=self.char2,
- )
- self.call(rpsystem.CmdSay(),"Hello!",'Char says, "Hello!"')
- self.call(rpsystem.CmdEmote(),"/me smiles to /BarFoo.","Char smiles to BarFoo Character")
- self.call(
- rpsystem.CmdPose(),
- "stands by the bar",
- "Pose will read 'Foobar Character stands by the bar.'.",
- )
- self.call(
- rpsystem.CmdRecog(),
- "barfoo as friend",
- "You will now remember BarFoo Character as friend.",
- )
- self.call(
- rpsystem.CmdRecog(),
- "",
- "Currently recognized (use 'recog <sdesc> as <alias>' to add new "
- "and 'forget <alias>' to remove):\n friend (BarFoo Character)",
- )
- self.call(
- rpsystem.CmdRecog(),
- "friend",
- "You will now know them only as 'BarFoo Character'",
- cmdstring="forget",
- )
[docs]deftest_cache(self):
- """
- Cache should not be set until first get
- """
- self.assertEqual(len(self.traithandler._cache),0)
- self.traithandler.all()# does not affect cache
- self.assertEqual(len(self.traithandler._cache),0)
- self.traithandler.test1
- self.assertEqual(len(self.traithandler._cache),1)
- self.traithandler.test2
- self.assertEqual(len(self.traithandler._cache),2)
[docs]deftest_boundaries__disable(self):
- """Disable and re-enable boundaries"""
- self.trait.base=5
- self.trait.mod=100
- self.assertEqual(self._get_values(),(5,5,1.0,10,0,10))
- delself.trait.max
- self.assertEqual(self.trait.max,None)
- delself.trait.min
- self.assertEqual(self.trait.min,None)
- self.trait.base=100
- self.assertEqual(self._get_values(),(100,5,1.0,105,None,None))
- self.trait.base=-200
- self.assertEqual(self._get_values(),(-200,5,1.0,-195,None,None))
-
- # re-activate boundaries
- self.trait.max=15
- self.trait.min=10# his is blocked since base+mod is lower
- self.assertEqual(self._get_values(),(-200,5,1.0,-195,-195,15))
-
-
[docs]deftest_boundaries__inverse(self):
- """Set inverse boundaries - limited by base"""
- self.trait.mod=0
- self.assertEqual(self._get_values(),(1,0,1.0,1,0,10))
- self.trait.min=20# will be set to base
- self.assertEqual(self._get_values(),(1,0,1.0,1,1,10))
- self.trait.max=-20
- self.assertEqual(self._get_values(),(1,0,1.0,1,1,1))
[docs]deftest_value(self):
- """value is current, where current defaults to base + mod"""
- # current unset - follows base + mod
- self.assertEqual(self._get_values(),(8,2,1.0,10,0,10))
- self.trait.base+=4
- self.assertEqual(self._get_values(),(12,2,1.0,14,0,14))
- self.trait.mod-=1
- self.assertEqual(self._get_values(),(12,1,1.0,13,0,13))
- self.trait.mult+=1.0
- self.assertEqual(self._get_values(),(12,1,2.0,26,0,26))
- # set current, decouple from base + mod
- self.trait.current=5
- self.assertEqual(self._get_values(),(12,1,2.0,5,0,26))
- self.trait.mod+=1
- self.trait.base-=4
- self.trait.mult-=1.0
- self.assertEqual(self._get_values(),(8,2,1.0,5,0,10))
- self.trait.min=-100
- self.trait.base=-20
- self.assertEqual(self._get_values(),(-20,2,1.0,-18,-100,-18))
-
-
[docs]deftest_boundaries__minmax(self):
- """Test range"""
- # current unset - tied to base + mod
- self.trait.base+=20
- self.assertEqual(self._get_values(),(28,2,1.0,30,0,30))
- # set current - decouple from base + mod
- self.trait.current=19
- self.assertEqual(self._get_values(),(28,2,1.0,19,0,30))
- # test upper bound
- self.trait.current=100
- self.assertEqual(self._get_values(),(28,2,1.0,30,0,30))
- # with multiplier
- self.trait.mult=2.0
- self.assertEqual(self._get_values(),(28,2,2.0,30,0,60))
- self.trait.current=100
- self.assertEqual(self._get_values(),(28,2,2.0,60,0,60))
- # min defaults to 0
- self.trait.mult=1.0
- self.trait.current=-10
- self.assertEqual(self._get_values(),(28,2,1.0,0,0,30))
- self.trait.min=-20
- self.assertEqual(self._get_values(),(28,2,1.0,0,-20,30))
- self.trait.current=-10
- self.assertEqual(self._get_values(),(28,2,1.0,-10,-20,30))
-
-
[docs]deftest_boundaries__bigmod(self):
- """add a big mod"""
- self.trait.base=5
- self.trait.mod=100
- self.assertEqual(self._get_values(),(5,100,1.0,105,0,105))
- # restricted by min
- self.trait.mod=-100
- self.assertEqual(self._get_values(),(5,-5,1.0,0,0,0))
- self.trait.min=-200
- self.assertEqual(self._get_values(),(5,-5,1.0,0,-200,0))
-
-
[docs]deftest_boundaries__change_boundaries(self):
- """Change boundaries after current change"""
- self.trait.current=20
- self.assertEqual(self._get_values(),(8,2,1.0,10,0,10))
- self.trait.mod=102
- self.assertEqual(self._get_values(),(8,102,1.0,10,0,110))
- # raising min past current value will force it upwards
- self.trait.min=20
- self.assertEqual(self._get_values(),(8,102,1.0,20,20,110))
[docs]deftest_boundaries__inverse(self):
- """Try to set reversed boundaries"""
- self.trait.mod=0
- self.trait.base=-10# limited by min
- self.assertEqual(self._get_values(),(0,0,1.0,0,0,0))
- self.trait.min=-10
- self.assertEqual(self._get_values(),(0,0,1.0,0,-10,0))
- self.trait.base=-10
- self.assertEqual(self._get_values(),(-10,0,1.0,-10,-10,-10))
- self.min=0# limited by base + mod
- self.assertEqual(self._get_values(),(-10,0,1.0,-10,-10,-10))
[docs]deftest_add_traits(self):
- """test addition of `Trait` objects"""
- # two Trait objects
- self.assertEqual(self.st+self.at,12)
- # Trait and numeric
- self.assertEqual(self.st+1,9)
- self.assertEqual(1+self.st,9)
-
-
[docs]deftest_sub_traits(self):
- """test subtraction of `Trait` objects"""
- # two Trait objects
- self.assertEqual(self.st-self.at,4)
- # Trait and numeric
- self.assertEqual(self.st-1,7)
- self.assertEqual(10-self.st,2)
-
-
[docs]deftest_mul_traits(self):
- """test multiplication of `Trait` objects"""
- # between two Traits
- self.assertEqual(self.st*self.at,32)
- # between Trait and numeric
- self.assertEqual(self.at*4,16)
- self.assertEqual(4*self.at,16)
-
-
[docs]deftest_floordiv(self):
- """test floor division of `Trait` objects"""
- # between two Traits
- self.assertEqual(self.st//self.at,2)
- # between Trait and numeric
- self.assertEqual(self.st//2,4)
- self.assertEqual(18//self.st,2)
[docs]deftearDown(self):
- super().tearDown()
- # if we forget to stop the script, DirtyReactorAggregateError will be raised
- self.script.stop()
-
-
[docs]deftest_at_repeat(self,mock_random):
- """test that no message will be sent when below the 66% threshold"""
- mock_random.random=Mock(return_value=0.5)
- old_func=self.script.send_random_message
- self.script.send_random_message=Mock()
- self.script.at_repeat()
- self.script.send_random_message.assert_not_called()
- # test that random message will be sent
- mock_random.random=Mock(return_value=0.7)
- self.script.at_repeat()
- self.script.send_random_message.assert_called()
- self.script.send_random_message=old_func
-
-
[docs]deftest_send_random_message(self,mock_random):
- """Test that correct message is sent for each random value"""
- old_func=self.char1.msg
- self.char1.msg=Mock()
- # test each of the values
- mock_random.random=Mock(return_value=0.05)
- self.script.send_random_message()
- self.char1.msg.assert_called_with("You tap your foot, looking around.")
- mock_random.random=Mock(return_value=0.15)
- self.script.send_random_message()
- self.char1.msg.assert_called_with("You have an itch. Hard to reach too.")
- mock_random.random=Mock(return_value=0.25)
- self.script.send_random_message()
- self.char1.msg.assert_called_with(
- "You think you hear someone behind you. ... ""but when you look there's noone there."
- )
- mock_random.random=Mock(return_value=0.35)
- self.script.send_random_message()
- self.char1.msg.assert_called_with("You inspect your fingernails. Nothing to report.")
- mock_random.random=Mock(return_value=0.45)
- self.script.send_random_message()
- self.char1.msg.assert_called_with("You cough discreetly into your hand.")
- mock_random.random=Mock(return_value=0.55)
- self.script.send_random_message()
- self.char1.msg.assert_called_with("You scratch your head, looking around.")
- mock_random.random=Mock(return_value=0.65)
- self.script.send_random_message()
- self.char1.msg.assert_called_with("You blink, forgetting what it was you were going to do.")
- mock_random.random=Mock(return_value=0.75)
- self.script.send_random_message()
- self.char1.msg.assert_called_with("You feel lonely all of a sudden.")
- mock_random.random=Mock(return_value=0.85)
- self.script.send_random_message()
- self.char1.msg.assert_called_with("You get a great idea. Of course you won't tell anyone.")
- mock_random.random=Mock(return_value=0.95)
- self.script.send_random_message()
- self.char1.msg.assert_called_with("You suddenly realize how much you love Evennia!")
- self.char1.msg=old_func
[docs]deftest_base_chargen(self):
- self.assertEqual(self.chargen.strength,10)# not realistic, due to mock
- self.assertEqual(self.chargen.armor,"gambeson")
- self.assertEqual(self.chargen.shield,"shield")
- self.assertEqual(
- self.chargen.backpack,["ration","ration","waterskin","waterskin","drill","twine"]
- )
-
-
[docs]deftest_build_desc(self):
- self.assertEqual(
- self.chargen.desc,
- "You are scrawny with a broken face, pockmarked skin, greased hair, hoarse speech, and "
- "stained clothing. You were a Herbalist, but you were exiled and ended up a knave. You "
- "are honest but also irascible. You tend towards neutrality.",
- )
[docs]classEvAdventureTurnbasedCombatHandlerTest(EvAdventureMixin,BaseEvenniaTest):
- """
- Test methods on the turn-based combat handler.
-
- """
-
- maxDiff=None
-
- # make sure to mock away all time-keeping elements
-
[docs]@patch("evennia.contrib.tutorials.evadventure.combat_turnbased.rules.randint")
- deftest_attack__miss(self,mock_randint):
- mock_randint.return_value=8# target has default armor 11, so 8+1 str will miss
- self._run_action(combat_turnbased.CombatActionAttack,self.target)
- self.assertEqual(self.target.hp,4)
-
-
[docs]@patch("evennia.contrib.tutorials.evadventure.combat_turnbased.rules.randint")
- deftest_attack__success__still_alive(self,mock_randint):
- mock_randint.return_value=11# 11 + 1 str will hit beat armor 11
- # make sure target survives
- self.target.hp=20
- self._run_action(combat_turnbased.CombatActionAttack,self.target)
- self.assertEqual(self.target.hp,9)
-
-
[docs]@patch("evennia.contrib.tutorials.evadventure.combat_turnbased.rules.randint")
- deftest_attack__success__kill(self,mock_randint):
- mock_randint.return_value=11# 11 + 1 str will hit beat armor 11
- self._run_action(combat_turnbased.CombatActionAttack,self.target)
- self.assertEqual(self.target.hp,-7)
- # after this the combat is over
- self.assertIsNone(self.combathandler.pk)
[docs]deftest_use_item(self):
- """
- Use up a potion during combat.
-
- """
- item=create.create_object(
- EvAdventureConsumable,key="Healing potion",attributes=[("uses",2)]
- )
- self.assertEqual(item.uses,2)
- self._run_action(combat_turnbased.CombatActionUseItem,item,self.combatant)
- self.assertEqual(item.uses,1)
- self._run_action(combat_turnbased.CombatActionUseItem,item,self.combatant)
- self.assertEqual(item.pk,None)# deleted, it was used up
-
-
[docs]deftest_swap_wielded_weapon_or_spell(self):
- """
- First draw a weapon (from empty fists), then swap that out to another weapon, then
- swap to a spell rune.
-
- """
- sword=create.create_object(EvAdventureWeapon,key="sword")
- zweihander=create.create_object(
- EvAdventureWeapon,
- key="zweihander",
- attributes=(("inventory_use_slot",WieldLocation.TWO_HANDS),),
- )
- runestone=create.create_object(EvAdventureRunestone,key="ice rune")
-
- # check hands are empty
- self.assertEqual(self.combatant.weapon.key,"Empty Fists")
- self.assertEqual(self.combatant.equipment.slots[WieldLocation.WEAPON_HAND],None)
- self.assertEqual(self.combatant.equipment.slots[WieldLocation.TWO_HANDS],None)
-
- # swap to sword
- self._run_action(combat_turnbased.CombatActionSwapWieldedWeaponOrSpell,None,sword)
- self.assertEqual(self.combatant.weapon,sword)
- self.assertEqual(self.combatant.equipment.slots[WieldLocation.WEAPON_HAND],sword)
- self.assertEqual(self.combatant.equipment.slots[WieldLocation.TWO_HANDS],None)
-
- # swap to zweihander (two-handed sword)
- self._run_action(combat_turnbased.CombatActionSwapWieldedWeaponOrSpell,None,zweihander)
- self.assertEqual(self.combatant.weapon,zweihander)
- self.assertEqual(self.combatant.equipment.slots[WieldLocation.WEAPON_HAND],None)
- self.assertEqual(self.combatant.equipment.slots[WieldLocation.TWO_HANDS],zweihander)
-
- # swap to runestone (also using two hands)
- self._run_action(combat_turnbased.CombatActionSwapWieldedWeaponOrSpell,None,runestone)
- self.assertEqual(self.combatant.weapon,runestone)
- self.assertEqual(self.combatant.equipment.slots[WieldLocation.WEAPON_HAND],None)
- self.assertEqual(self.combatant.equipment.slots[WieldLocation.TWO_HANDS],runestone)
-
- # swap back to normal one-handed sword
- self._run_action(combat_turnbased.CombatActionSwapWieldedWeaponOrSpell,None,sword)
- self.assertEqual(self.combatant.weapon,sword)
- self.assertEqual(self.combatant.equipment.slots[WieldLocation.WEAPON_HAND],sword)
- self.assertEqual(self.combatant.equipment.slots[WieldLocation.TWO_HANDS],None)
-
-
[docs]deftest_flee__success(self):
- """
- Test fleeing twice, leading to leaving combat.
-
- """
- # first flee records the fleeing state
- self._run_action(combat_turnbased.CombatActionFlee,None)
- self.assertTrue(self.combatantinself.combathandler.fleeing_combatants)
-
- # second flee should remove combatant
- self._run_action(combat_turnbased.CombatActionFlee,None)
- self.assertIsNone(self.combathandler.pk)
-
-
[docs]@patch("evennia.contrib.tutorials.evadventure.combat_turnbased.rules.randint")
- deftest_flee__blocked(self,mock_randint):
- """ """
- mock_randint.return_value=11# means block will succeed
-
- self._run_action(combat_turnbased.CombatActionFlee,None)
- self.assertTrue(self.combatantinself.combathandler.fleeing_combatants)
-
- # other combatant blocks in the same turn
- self.combathandler.register_action(
- self.combatant,combat_turnbased.CombatActionFlee.key,None
- )
- self.combathandler.register_action(
- self.target,combat_turnbased.CombatActionBlock.key,self.combatant
- )
- self.combathandler._end_turn()
- # the fleeing combatant should remain now
- self.assertTrue(self.combatantnotinself.combathandler.fleeing_combatants)
- self.assertTrue(self.combatantinself.combathandler.combatants)
[docs]defsetUp(self):
- super().setUp()
- # needed for the .call mechanism
- self.char1=self.character
-
-
[docs]deftest_inventory(self):
- self.call(
- commands.CmdInventory(),
- "inventory",
- """
-You are fighting with your bare fists and have no shield.
-You wear no armor and no helmet.
-Backpack is empty.
-You use 0/11 equipment slots.
-""".strip(),
- )
[docs]deftest_wield_or_wear(self):
- self.char1.equipment.add(self.helmet)
- self.char1.equipment.add(self.weapon)
- self.shield.location=self.location
-
- self.call(commands.CmdWieldOrWear(),"shield","Could not find 'shield'")
- self.call(commands.CmdWieldOrWear(),"helmet","You put helmet on your head.")
- self.call(
- commands.CmdWieldOrWear(),
- "weapon",
- "You hold weapon in your strongest hand, ready for action.",
- )
- self.call(commands.CmdWieldOrWear(),"helmet","You are already using helmet.")
-
-
[docs]deftest_remove(self):
- self.char1.equipment.add(self.helmet)
- self.call(commands.CmdWieldOrWear(),"helmet","You put helmet on your head.")
-
- self.call(commands.CmdRemove(),"helmet","You stash helmet in your backpack.")
-
-
[docs]deftest_give__coins(self):
- recipient=create_object(EvAdventureCharacter,key="Friend",location=self.location)
- recipient.coins=0
- self.char1.coins=100
-
- self.call(commands.CmdGive(),"40 coins to friend","You give Friend 40 coins.")
- self.assertEqual(self.char1.coins,60)
- self.assertEqual(recipient.coins,40)
-
- self.call(commands.CmdGive(),"10 to friend","You give Friend 10 coins.")
- self.assertEqual(self.char1.coins,50)
- self.assertEqual(recipient.coins,50)
-
- self.call(commands.CmdGive(),"60 to friend","You only have 50 coins to give.")
-
- recipient.delete()
[docs]classTestDungeon(EvAdventureMixin,BaseEvenniaTest):
- """
- Test with a starting room and a character moving through the dungeon,
- generating more and more rooms as they go.
-
- """
-
-
[docs]defsetUp(self):
- """
- Create a start room with exits leading away from it
-
- """
- super().setUp()
- droomclass=dungeon.EvAdventureDungeonStartRoom
- droomclass.recycle_time=0# disable the tick
- droomclass.branch_check_time=0
-
- self.start_room=create_object(droomclass,key="bottom of well")
-
- self.assertEqual(
- self.start_room.scripts.get("evadventure_dungeon_startroom_resetter")[0].interval,-1
- )
- self.start_north=create_object(
- dungeon.EvAdventureDungeonStartRoomExit,
- key="north",
- location=self.start_room,
- destination=self.start_room,
- )
- self.start_north
- self.start_south=create_object(
- dungeon.EvAdventureDungeonStartRoomExit,
- key="south",
- location=self.start_room,
- destination=self.start_room,
- )
- self.character.location=self.start_room
-
- def_move_character(self,direction):
- old_location=self.character.location
- forexiinold_location.exits:
- ifexi.key==direction:
- # by setting target to old-location we trigger the
- # special behavior of this Exit type
- exi.at_traverse(self.character,exi.destination)
- break
- returnself.character.location
-
-
[docs]deftest_start_room(self):
- """
- Test move through one of the start room exits.
-
- """
- # begin in start room
- self.assertEqual(self.character.location,self.start_room)
-
- # first go north, this should generate a new room
- new_room_north=self._move_character("north")
- self.assertNotEqual(self.start_room,new_room_north)
- self.assertTrue(inherits_from(new_room_north,dungeon.EvAdventureDungeonRoom))
-
- # check if Orchestrator was created
- orchestrator=new_room_north.db.dungeon_orchestrator
- self.assertTrue(bool(orchestrator))
- self.assertTrue(orchestrator.key.startswith("dungeon_orchestrator_north_"))
-
-
[docs]deftest_different_start_directions(self):
- # first go north, this should generate a new room
- new_room_north=self._move_character("north")
- self.assertNotEqual(self.start_room,new_room_north)
-
- # back to start room
- start_room=self._move_character("south")
- self.assertEqual(self.start_room,start_room)
-
- # next go south, this should generate a new room
- new_room_south=self._move_character("south")
- self.assertNotEqual(self.start_room,new_room_south)
- self.assertNotEqual(new_room_north,new_room_south)
-
- # back to start room again
- start_room=self._move_character("north")
- self.assertEqual(self.start_room,start_room)
Source code for evennia.contrib.tutorials.evadventure.tests.test_quests
-"""
-Testing Quest functionality.
-
-"""
-
-fromunittest.mockimportMagicMock
-
-fromevennia.utils.test_resourcesimportBaseEvenniaTest
-
-from..importquests
-from..objectsimportEvAdventureObject
-from.mixinsimportEvAdventureMixin
-
-
-class_TestQuest(quests.EvAdventureQuest):
- """
- Test quest.
-
- """
-
- key="testquest"
- desc="A test quest!"
-
- start_step="A"
- end_text="This task is completed."
-
- help_A="You need to do A first."
- help_B="Next, do B."
-
- defstep_A(self,*args,**kwargs):
- """
- Quest-step A is completed when quester carries an item with tag "QuestA" and category
- "quests".
- """
- # note - this could be done with a direct db query instead to avoid a loop, for a
- # unit test it's fine though
- ifany(objforobjinself.quester.contentsifobj.tags.has("QuestA",category="quests")):
- self.quester.msg("Completed step A of quest!")
- self.current_step="B"
- self.progress()
-
- defstep_B(self,*args,**kwargs):
- """
- Quest-step B is completed when the progress-check is called with a special kwarg
- "complete_quest_B"
-
- """
- ifkwargs.get("complete_quest_B",False):
- self.quester.msg("Completed step B of quest!")
- self.quester.db.test_quest_counter=0
- self.current_step="C"
- self.progress()
-
- defhelp_C(self):
- """Testing the method-version of getting a help entry"""
- returnf"Only C left now, {self.quester.key}!"
-
- defstep_C(self,*args,**kwargs):
- """
- Step C (final) step of quest completes when a counter on quester is big enough.
-
- """
- ifself.quester.db.test_quest_counterandself.quester.db.test_quest_counter>5:
- self.quester.msg("Quest complete! Get XP rewards!")
- self.quester.db.xp+=10
- self.complete()
-
- defcleanup(self):
- """
- Cleanup data related to quest.
-
- """
- delself.quester.db.test_quest_counter
-
-
-
[docs]deftest_help(self):
- """Get help"""
- # get help for all quests
- help_txt=self.character.quests.get_help()
- self.assertEqual(help_txt,["|ctestquest|n\n A test quest!\n\n - You need to do A first."])
-
- # get help for one specific quest
- help_txt=self.character.quests.get_help(_TestQuest.key)
- self.assertEqual(help_txt,["|ctestquest|n\n A test quest!\n\n - You need to do A first."])
-
- # help for finished quest
- self._get_quest().is_completed=True
- help_txt=self.character.quests.get_help()
- self.assertEqual(help_txt,["|ctestquest|n\n A test quest!\n\n - This quest is completed!"])
-
-
[docs]deftest_progress__fail(self):
- """
- Check progress without having any.
- """
- # progress all quests
- self.character.quests.progress()
- # progress one quest
- self.character.quests.progress(_TestQuest.key)
-
- # still on step A
- self.assertEqual(self._get_quest().current_step,"A")
-
-
[docs]deftest_progress(self):
- """
- Fulfill the quest steps in sequess
-
- """
- # A requires a certain object in inventory
- self._fulfillA()
- self.character.quests.progress()
- self.assertEqual(self._get_quest().current_step,"B")
-
- # B requires progress be called with specific kwarg
- # should not step (no kwarg)
- self.character.quests.progress()
- self.assertEqual(self._get_quest().current_step,"B")
-
- # should step (kwarg sent)
- self.character.quests.progress(complete_quest_B=True)
- self.assertEqual(self._get_quest().current_step,"C")
-
- # C requires a counter Attribute on char be high enough
- self._fulfillC()
- self.character.quests.progress()
- self.assertEqual(self._get_quest().current_step,"C")# still on last step
- self.assertEqual(self._get_quest().is_completed,True)
[docs]@patch("evennia.contrib.tutorials.evadventure.rules.randint")
- deftest_roll_random_table(self,mock_randint):
- mock_randint.return_value=10
-
- self.assertEqual(
- self.roll_engine.roll_random_table("1d20",random_tables.chargen_tables["physique"]),
- "scrawny",
- )
- self.assertEqual(
- self.roll_engine.roll_random_table("1d20",random_tables.chargen_tables["vice"]),
- "irascible",
- )
- self.assertEqual(
- self.roll_engine.roll_random_table("1d20",random_tables.chargen_tables["alignment"]),
- "neutrality",
- )
- self.assertEqual(
- self.roll_engine.roll_random_table(
- "1d20",random_tables.chargen_tables["helmets and shields"]
- ),
- "no helmet or shield",
- )
- # testing faulty rolls outside of the table ranges
- mock_randint.return_value=25
- self.assertEqual(
- self.roll_engine.roll_random_table(
- "1d20",random_tables.chargen_tables["helmets and shields"]
- ),
- "helmet and shield",
- )
- mock_randint.return_value=-10
- self.assertEqual(
- self.roll_engine.roll_random_table(
- "1d20",random_tables.chargen_tables["helmets and shields"]
- ),
- "no helmet or shield",
- )
-
-
[docs]@patch("evennia.contrib.tutorials.evadventure.rules.randint")
- deftest_morale_check(self,mock_randint):
- defender=MagicMock()
- defender.morale=12
-
- mock_randint.return_value=7# 2d6 is rolled, so this will become 14
- self.assertEqual(self.roll_engine.morale_check(defender),False)
-
- mock_randint.return_value=3# 2d6 is rolled, so this will become 6
- self.assertEqual(self.roll_engine.morale_check(defender),True)
[docs]deftest_talkingnpc(self):
- npc=create_object(talking_npc.TalkingNPC,key="npctalker",location=self.room1)
- self.call(talking_npc.CmdTalk(),"","(You walk up and talk to Char.)")
- npc.delete()
[docs]deftest_readable(self):
- readable=create_object(tutobjects.TutorialReadable,key="book",location=self.room1)
- readable.db.readable_text="Text to read"
- self.call(tutobjects.CmdRead(),"book","You read book:\n Text to read",obj=readable)
[docs]deftest_bridgeroom(self):
- room=create_object(tutrooms.BridgeRoom,key="bridgeroom")
- room.update_weather()
- self.char1.move_to(room,move_type="teleport")
- self.call(
- tutrooms.CmdBridgeHelp(),
- "",
- "You are trying hard not to fall off the bridge ...",
- obj=room,
- )
- self.call(
- tutrooms.CmdLookBridge(),
- "",
- "bridgeroom\nYou are standing very close to the the bridge's western foundation.",
- obj=room,
- )
- room.at_object_leave(self.char1,self.room1)
- tutrooms.TICKER_HANDLER.remove(
- interval=room.db.interval,callback=room.update_weather,idstring="tutorial"
- )
- room.delete()
-
-
[docs]deftest_darkroom(self):
- room=create_object(tutrooms.DarkRoom,key="darkroom")
- self.char1.move_to(room,move_type="teleport")
- self.call(tutrooms.CmdDarkHelp(),"","Can't help you until")
[docs]@patch("evennia.server.sessionhandler._ServerSession",AuditedServerSession)
- defsetup_session(self):
- """Overrides default one in EvenniaTest"""
- dummysession=AuditedServerSession()
- dummysession.init_session("telnet",("localhost","testmode"),SESSIONS)
- dummysession.sessid=1
- SESSIONS.portal_connect(
- dummysession.get_sync_data()
- )# note that this creates a new Session!
- session=SESSIONS.session_from_sessid(1)# the real session
- SESSIONS.login(session,self.account,testmode=True)
- self.session=session
-
-
[docs]@patch(
- "evennia.contrib.utils.auditing.server.AUDIT_CALLBACK",
- "evennia.contrib.utils.auditing.outputs.to_syslog",
- )
- @patch("evennia.contrib.utils.auditing.server.AUDIT_IN",True)
- @patch("evennia.contrib.utils.auditing.server.AUDIT_OUT",True)
- deftest_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.game_systems.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."}',
- )
-
- forcmdinsafe_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.'}",
- ),
- )
-
- forindex,(unsafe,safe)inenumerate(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__':{}})",
- ]
- forsecretinsecrets:
- self.assertEqual(self.session.mask(secret),secret)
-
-
[docs]@patch(
- "evennia.contrib.utils.auditing.server.AUDIT_CALLBACK",
- "evennia.contrib.utils.auditing.outputs.to_syslog",
- )
- @patch("evennia.contrib.utils.auditing.server.AUDIT_IN",True)
- @patch("evennia.contrib.utils.auditing.server.AUDIT_OUT",True)
- deftest_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:vfork,vinlog.items()ifkin("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)
[docs]deftest_git_checkout(self):
- # Checkout no branch
- self.test_cmd_git.checkout()
- self.char1.msg.assert_called_with("Branch 'nonexistent_branch' not available.")
-
-
[docs]deftest_git_pull(self):
- self.test_cmd_git.pull()
- self.char1.msg.assert_called_with(
- f"You have pulled new code. Server restart initiated.|/Head now at {self.repo.git.rev_parse(self.repo.head.commit.hexsha,short=True)}.|/Author: {self.repo.head.commit.author.name} ({self.repo.head.commit.author.email})|/{self.repo.head.commit.message.strip()}"
- )
[docs]deftest_generate(self):
- """Generate and fail when exhausted."""
- generated=[]
- foriinrange(4):
- generated.append(SIMPLE_GENERATOR.get())
-
- generated.sort()
- self.assertEqual(generated,["00","01","10","11"])
-
- # At this point, we have generated 4 strings.
- # We can't generate one more
- withself.assertRaises(random_string_generator.ExhaustedGenerator):
- SIMPLE_GENERATOR.get()
-
-
-
\ No newline at end of file
diff --git a/docs/1.0-dev/_modules/evennia/objects/objects.html b/docs/1.0-dev/_modules/evennia/objects/objects.html
index 721047ef83..8f6a05755e 100644
--- a/docs/1.0-dev/_modules/evennia/objects/objects.html
+++ b/docs/1.0-dev/_modules/evennia/objects/objects.html
@@ -1317,7 +1317,8 @@
looker (Object): Onlooker. Not used by default. Keyword Args:
- key (str): Optional key to pluralize, if given, use this instead of the object's key.
+ key (str): Optional key to pluralize. If not given, the object's `.name` property is
+ used. Returns: tuple: This is a tuple `(str, str)` with the singular and plural forms of the key
@@ -1329,7 +1330,7 @@
"""plural_category="plural_key"
- key=kwargs.get("key",self.key)
+ key=kwargs.get("key",self.name)key=ansi.ANSIString(key)# this is needed to allow inflection of colored namestry:plural=_INFLECT.plural(key,count)
diff --git a/docs/1.0-dev/_modules/evennia/server/portal/tests.html b/docs/1.0-dev/_modules/evennia/server/portal/tests.html
deleted file mode 100644
index 0a76f0d8d5..0000000000
--- a/docs/1.0-dev/_modules/evennia/server/portal/tests.html
+++ /dev/null
@@ -1,431 +0,0 @@
-
-
-
-
-
-
-
- evennia.server.portal.tests — Evennia 1.0-dev documentation
-
-
-
-
-
-
-
-
-
-
-
-
[docs]deftest_plain_ansi(self):
- """
- Test that printable characters do not get mangled.
- """
- irc_ansi=irc.parse_ansi_to_irc(string.printable)
- ansi_irc=irc.parse_irc_to_ansi(string.printable)
- self.assertEqual(irc_ansi,string.printable)
- self.assertEqual(ansi_irc,string.printable)
[docs]deftest_identity(self):
- """
- Test that the composition of the function and
- its inverse gives the correct string.
- """
-
- s=r"|wthis|Xis|gis|Ma|C|complex|*string"
-
- self.assertEqual(irc.parse_irc_to_ansi(irc.parse_ansi_to_irc(s)),s)
[docs]deftest_c_creates_obj(self):
- objname="testing_obj_1"
- self.assertEqual(
- c_creates_obj(self.client),
- (
- "create %s"%objname,
- 'desc %s = "this is a test object'%objname,
- "set %s/testattr = this is a test attribute value."%objname,
- "set %s/testattr2 = this is a second test attribute."%objname,
- ),
- )
- self.assertEqual(self.client.objs,[objname])
- self.clear_client_lists()
-"""
-Various helper resources for writing unittests.
-
-Classes for testing Evennia core:
-
-- `BaseEvenniaTestCase` - no default objects, only enforced default settings
-- `BaseEvenniaTest` - all default objects, enforced default settings
-- `BaseEvenniaCommandTest` - for testing Commands, enforced default settings
-
-Classes for testing game folder content:
-
-- `EvenniaTestCase` - no default objects, using gamedir settings (identical to
- standard Python TestCase)
-- `EvenniaTest` - all default objects, using gamedir settings
-- `EvenniaCommandTest` - for testing game folder commands, using gamedir settings
-
-Other:
-
-- `EvenniaTestMixin` - A class mixin for creating the test environment objects, for
- making custom tests.
-- `EvenniaCommandMixin` - A class mixin that adds support for command testing with the .call()
- helper. Used by the command-test classes, but can be used for making a customt test class.
-
-"""
-importre
-importsys
-importtypes
-
-fromdjango.confimportsettings
-fromdjango.testimportTestCase,override_settings
-frommockimportMagicMock,Mock,patch
-fromtwisted.internet.deferimportDeferred
-
-fromevenniaimportsettings_default
-fromevennia.accounts.accountsimportDefaultAccount
-fromevennia.commands.commandimportInterruptCommand
-fromevennia.commands.default.muxcommandimportMuxCommand
-fromevennia.objects.objectsimport(
- DefaultCharacter,
- DefaultExit,
- DefaultObject,
- DefaultRoom,
-)
-fromevennia.scripts.scriptsimportDefaultScript
-fromevennia.server.serversessionimportServerSession
-fromevennia.server.sessionhandlerimportSESSIONS
-fromevennia.utilsimportansi,create
-fromevennia.utils.idmapper.modelsimportflush_cache
-fromevennia.utils.utilsimportall_from_module,to_str
-
-_RE_STRIP_EVMENU=re.compile(r"^\+|-+\+|\+-+|--+|\|(?:\s|$)",re.MULTILINE)
-
-
-# set up a 'pristine' setting, unaffected by any changes in mygame
-DEFAULT_SETTING_RESETS=dict(
- CONNECTION_SCREEN_MODULE="evennia.game_template.server.conf.connection_screens",
- AT_SERVER_STARTSTOP_MODULE="evennia.game_template.server.conf.at_server_startstop",
- AT_SERVICES_PLUGINS_MODULES=["evennia.game_template.server.conf.server_services_plugins"],
- PORTAL_SERVICES_PLUGIN_MODULES=["evennia.game_template.server.conf.portal_services_plugins"],
- MSSP_META_MODULE="evennia.game_template.server.conf.mssp",
- WEB_PLUGINS_MODULE="server.conf.web_plugins",
- LOCK_FUNC_MODULES=("evennia.locks.lockfuncs","evennia.game_template.server.conf.lockfuncs"),
- INPUT_FUNC_MODULES=[
- "evennia.server.inputfuncs",
- "evennia.game_template.server.conf.inputfuncs",
- ],
- PROTOTYPE_MODULES=["evennia.game_template.world.prototypes"],
- CMDSET_UNLOGGEDIN="evennia.game_template.commands.default_cmdsets.UnloggedinCmdSet",
- CMDSET_SESSION="evennia.game_template.commands.default_cmdsets.SessionCmdSet",
- CMDSET_CHARACTER="evennia.game_template.commands.default_cmdsets.CharacterCmdSet",
- CMDSET_ACCOUNT="evennia.game_template.commands.default_cmdsets.AccountCmdSet",
- CMDSET_PATHS=["evennia.game_template.commands","evennia","evennia.contrib"],
- TYPECLASS_PATHS=[
- "evennia",
- "evennia.contrib",
- "evennia.contrib.game_systems",
- "evennia.contrib.base_systems",
- "evennia.contrib.full_systems",
- "evennia.contrib.tutorials",
- "evennia.contrib.utils",
- ],
- BASE_ACCOUNT_TYPECLASS="evennia.accounts.accounts.DefaultAccount",
- BASE_OBJECT_TYPECLASS="evennia.objects.objects.DefaultObject",
- BASE_CHARACTER_TYPECLASS="evennia.objects.objects.DefaultCharacter",
- BASE_ROOM_TYPECLASS="evennia.objects.objects.DefaultRoom",
- BASE_EXIT_TYPECLASS="evennia.objects.objects.DefaultExit",
- BASE_CHANNEL_TYPECLASS="evennia.comms.comms.DefaultChannel",
- BASE_SCRIPT_TYPECLASS="evennia.scripts.scripts.DefaultScript",
- BASE_BATCHPROCESS_PATHS=[
- "evennia.game_template.world",
- "evennia.contrib",
- "evennia.contrib.tutorials",
- ],
- FILE_HELP_ENTRY_MODULES=["evennia.game_template.world.help_entries"],
- FUNCPARSER_OUTGOING_MESSAGES_MODULES=[
- "evennia.utils.funcparser",
- "evennia.game_template.server.conf.inlinefuncs",
- ],
- FUNCPARSER_PROTOTYPE_PARSING_MODULES=[
- "evennia.prototypes.protfuncs",
- "evennia.game_template.server.conf.prototypefuncs",
- ],
- BASE_GUEST_TYPECLASS="evennia.accounts.accounts.DefaultGuest",
- # a special setting boolean _TEST_ENVIRONMENT is set by the test runner
- # while the test suite is running.
-)
-
-DEFAULT_SETTINGS={**all_from_module(settings_default),**DEFAULT_SETTING_RESETS}
-DEFAULT_SETTINGS.pop("DATABASES")# we want different dbs tested in CI
-
-
-# mocking of evennia.utils.utils.delay
-
[docs]defunload_module(module):
- """
- Reset import so one can mock global constants.
-
- Args:
- module (module, object or str): The module will
- be removed so it will have to be imported again. If given
- an object, the module in which that object sits will be unloaded. A string
- should directly give the module pathname to unload.
-
- Example:
-
- ```python
- # (in a test method)
- unload_module(foo)
- with mock.patch("foo.GLOBALTHING", "mockval"):
- import foo
- ... # test code using foo.GLOBALTHING, now set to 'mockval'
- ```
-
- This allows for mocking constants global to the module, since
- otherwise those would not be mocked (since a module is only
- loaded once).
-
- """
- ifisinstance(module,str):
- modulename=module
- elifhasattr(module,"__module__"):
- modulename=module.__module__
- else:
- modulename=module.__name__
-
- ifmodulenameinsys.modules:
- delsys.modules[modulename]
[docs]defsetup_session(self):
- dummysession=ServerSession()
- dummysession.init_session("telnet",("localhost","testmode"),SESSIONS)
- dummysession.sessid=1
- SESSIONS.portal_connect(
- dummysession.get_sync_data()
- )# note that this creates a new Session!
- session=SESSIONS.session_from_sessid(1)# the real session
- SESSIONS.login(session,self.account,testmode=True)
- self.session=session
[docs]deftearDown(self):
- flush_cache()
- try:
- SESSIONS.data_out=self.backups[0]
- SESSIONS.disconnect=self.backups[1]
- settings.DEFAULT_HOME=self.backups[2]
- settings.PROTOTYPE_MODULES=self.backups[3]
- exceptAttributeErroraserr:
- raiseAttributeError(
- f"{err}: Teardown error. If you overrode the `setUp()` method "
- "in your test, make sure you also added `super().setUp()`!"
- )
-
- delSESSIONS[self.session.sessid]
- self.teardown_accounts()
- super().tearDown()
-
-
-
[docs]@patch("evennia.server.portal.portal.LoopingCall",new=MagicMock())
-classEvenniaCommandTestMixin:
- """
- Mixin to add to a test in order to provide the `.call` helper for
- testing the execution and returns of a command.
-
- Tests a Command by running it and comparing what messages it sends with
- expected values. This tests without actually spinning up the cmdhandler
- for every test, which is more controlled.
-
- Example:
- ::
-
- from commands.echo import CmdEcho
-
- class MyCommandTest(EvenniaTest, CommandTestMixin):
-
- def test_echo(self):
- '''
- Test that the echo command really returns
- what you pass into it.
- '''
- self.call(MyCommand(), "hello world!",
- "You hear your echo: 'Hello world!'")
-
- """
-
- # formatting for .call's error message
- _ERROR_FORMAT="""
-=========================== Wanted message ===================================
-{expected_msg}
-=========================== Returned message =================================
-{returned_msg}
-==============================================================================
-""".rstrip()
-
-
[docs]defcall(
- self,
- cmdobj,
- input_args,
- msg=None,
- cmdset=None,
- noansi=True,
- caller=None,
- receiver=None,
- cmdstring=None,
- obj=None,
- inputs=None,
- raw_string=None,
- ):
- """
- Test a command by assigning all the needed properties to a cmdobj and
- running the sequence. The resulting `.msg` calls will be mocked and
- the text= calls to them compared to a expected output.
-
- Args:
- cmdobj (Command): The command object to use.
- input_args (str): This should be the full input the Command should
- see, such as 'look here'. This will become `.args` for the Command
- instance to parse.
- msg (str or dict, optional): This is the expected return value(s)
- returned through `caller.msg(text=...)` calls in the command. If a string, the
- receiver is controlled with the `receiver` kwarg (defaults to `caller`).
- If this is a `dict`, it is a mapping
- `{receiver1: "expected1", receiver2: "expected2",...}` and `receiver` is
- ignored. The message(s) are compared with the actual messages returned
- to the receiver(s) as the Command runs. Each check uses `.startswith`,
- so you can choose to only include the first part of the
- returned message if that's enough to verify a correct result. EvMenu
- decorations (like borders) are stripped and should not be included. This
- should also not include color tags unless `noansi=False`.
- If the command returns texts in multiple separate `.msg`-
- calls to a receiver, separate these with `|` if `noansi=True`
- (default) and `||` if `noansi=False`. If no `msg` is given (`None`),
- then no automatic comparison will be done.
- cmdset (str, optional): If given, make `.cmdset` available on the Command
- instance as it runs. While `.cmdset` is normally available on the
- Command instance by default, this is usually only used by
- commands that explicitly operates/displays cmdsets, like
- `examine`.
- noansi (str, optional): By default the color tags of the `msg` is
- ignored, this makes them significant. If unset, `msg` must contain
- the same color tags as the actual return message.
- caller (Object or Account, optional): By default `self.char1` is used as the
- command-caller (the `.caller` property on the Command). This allows to
- execute with another caller, most commonly an Account.
- receiver (Object or Account, optional): This is the object to receive the
- return messages we want to test. By default this is the same as `caller`
- (which in turn defaults to is `self.char1`). Note that if `msg` is
- a `dict`, this is ignored since the receiver is already specified there.
- cmdstring (str, optional): Normally this is the Command's `key`.
- This allows for tweaking the `.cmdname` property of the
- Command`. This isb used for commands with multiple aliases,
- where the command explicitly checs which alias was used to
- determine its functionality.
- obj (str, optional): This sets the `.obj` property of the Command - the
- object on which the Command 'sits'. By default this is the same as `caller`.
- This can be used for testing on-object Command interactions.
- inputs (list, optional): A list of strings to pass to functions that pause to
- take input from the user (normally using `@interactive` and
- `ret = yield(question)` or `evmenu.get_input`). Each element of the
- list will be passed into the command as if the user answered each prompt
- in that order.
- raw_string (str, optional): Normally the `.raw_string` property is set as
- a combination of your `key/cmdname` and `input_args`. This allows
- direct control of what this is, for example for testing edge cases
- or malformed inputs.
-
- Returns:
- str or dict: The message sent to `receiver`, or a dict of
- `{receiver: "msg", ...}` if multiple are given. This is usually
- only used with `msg=None` to do the validation externally.
-
- Raises:
- AssertionError: If the returns of `.msg` calls (tested with `.startswith`) does not
- match `expected_input`.
-
- Notes:
- As part of the tests, all methods of the Command will be called in
- the proper order:
-
- - cmdobj.at_pre_cmd()
- - cmdobj.parse()
- - cmdobj.func()
- - cmdobj.at_post_cmd()
-
- """
- # The `self.char1` is created in the `EvenniaTest` base along with
- # other helper objects like self.room and self.obj
- caller=callerifcallerelseself.char1
- cmdobj.caller=caller
- cmdobj.cmdname=cmdstringifcmdstringelsecmdobj.key
- cmdobj.raw_cmdname=cmdobj.cmdname
- cmdobj.cmdstring=cmdobj.cmdname# deprecated
- cmdobj.args=input_args
- cmdobj.cmdset=cmdset
- cmdobj.session=SESSIONS.session_from_sessid(1)
- cmdobj.account=self.account
- cmdobj.raw_string=raw_stringifraw_stringisnotNoneelsecmdobj.key+" "+input_args
- cmdobj.obj=objor(callerifcallerelseself.char1)
- inputs=inputsor[]
-
- # set up receivers
- receiver_mapping={}
- ifisinstance(msg,dict):
- # a mapping {receiver: msg, ...}
- receiver_mapping={
- recv:str(msg).strip()ifmsgelseNoneforrecv,msginmsg.items()
- }
- else:
- # a single expected string and thus a single receiver (defaults to caller)
- receiver=receiverifreceiverelsecaller
- receiver_mapping[receiver]=str(msg).strip()ifmsgisnotNoneelseNone
-
- unmocked_msg_methods={}
- forreceiverinreceiver_mapping:
- # save the old .msg method so we can get it back
- # cleanly after the test
- unmocked_msg_methods[receiver]=receiver.msg
- # replace normal `.msg` with a mock
- receiver.msg=Mock()
-
- # Run the methods of the Command. This mimics what happens in the
- # cmdhandler. This will have the mocked .msg be called as part of the
- # execution. Mocks remembers what was sent to them so we will be able
- # to retrieve what was sent later.
- try:
- ifcmdobj.at_pre_cmd():
- return
- cmdobj.parse()
- ret=cmdobj.func()
-
- # handle func's with yield in them (making them generators)
- ifisinstance(ret,types.GeneratorType):
- whileTrue:
- try:
- inp=inputs.pop()ifinputselseNone
- ifinp:
- try:
- # this mimics a user's reply to a prompt
- ret.send(inp)
- exceptTypeError:
- next(ret)
- ret=ret.send(inp)
- else:
- # non-input yield, like yield(10). We don't pause
- # but fire it immediately.
- next(ret)
- exceptStopIteration:
- break
-
- cmdobj.at_post_cmd()
- exceptStopIteration:
- pass
- exceptInterruptCommand:
- pass
-
- forinpininputs:
- # if there are any inputs left, we may have a non-generator
- # input to handle (get_input/ask_yes_no that uses a separate
- # cmdset rather than a yield
- caller.execute_cmd(inp)
-
- # At this point the mocked .msg methods on each receiver will have
- # stored all calls made to them (that's a basic function of the Mock
- # class). We will not extract them and compare to what we expected to
- # go to each receiver.
-
- returned_msgs={}
- forreceiver,expected_msginreceiver_mapping.items():
- # get the stored messages from the Mock with Mock.mock_calls.
- stored_msg=[
- args[0]ifargsandargs[0]elsekwargs.get("text",to_str(kwargs))
- forname,args,kwargsinreceiver.msg.mock_calls
- ]
- # we can return this now, we are done using the mock
- receiver.msg=unmocked_msg_methods[receiver]
-
- # Get the first element of a tuple if msg received a tuple instead of a string
- stored_msg=[
- str(smsg[0])ifisinstance(smsg,tuple)elsestr(smsg)forsmsginstored_msg
- ]
- ifexpected_msgisNone:
- # no expected_msg; just build the returned_msgs dict
-
- returned_msg="\n".join(str(msg)formsginstored_msg)
- returned_msgs[receiver]=ansi.parse_ansi(returned_msg,strip_ansi=noansi).strip()
- else:
- # compare messages to expected
-
- # set our separator for returned messages based on parsing ansi or not
- msg_sep="|"ifnoansielse"||"
-
- # We remove Evmenu decorations since that just makes it harder
- # to write the comparison string. We also strip ansi before this
- # comparison since otherwise it would mess with the regex.
- returned_msg=msg_sep.join(
- _RE_STRIP_EVMENU.sub("",ansi.parse_ansi(mess,strip_ansi=noansi))
- formessinstored_msg
- ).strip()
-
- # this is the actual test
- ifexpected_msg==""andreturned_msgornotreturned_msg.startswith(expected_msg):
- # failed the test
- raiseAssertionError(
- self._ERROR_FORMAT.format(
- expected_msg=expected_msg,returned_msg=returned_msg
- )
- )
- # passed!
- returned_msgs[receiver]=returned_msg
-
- iflen(returned_msgs)==1:
- returnlist(returned_msgs.values())[0]
- returnreturned_msgs
-
-
-# Base testing classes
-
-
-
[docs]@override_settings(**DEFAULT_SETTINGS)
-classBaseEvenniaTestCase(TestCase):
- """
- Base test (with no default objects) but with enforced default settings.
-
- """
-
-
-
[docs]classEvenniaTestCase(TestCase):
- """
- For use with gamedir settings; Just like the normal test case, only for naming consistency.
-
- """
-
- pass
-
-
-
[docs]@override_settings(**DEFAULT_SETTINGS)
-classBaseEvenniaTest(EvenniaTestMixin,TestCase):
- """
- This class parent has all default objects and uses only default settings.
-
- """
-
-
-
[docs]classEvenniaTest(EvenniaTestMixin,TestCase):
- """
- This test class is intended for inheriting in mygame tests.
- It helps ensure your tests are run with your own objects
- and settings from your game folder.
-
- """
-
- account_typeclass=settings.BASE_ACCOUNT_TYPECLASS
- object_typeclass=settings.BASE_OBJECT_TYPECLASS
- character_typeclass=settings.BASE_CHARACTER_TYPECLASS
- exit_typeclass=settings.BASE_EXIT_TYPECLASS
- room_typeclass=settings.BASE_ROOM_TYPECLASS
- script_typeclass=settings.BASE_SCRIPT_TYPECLASS
-
-
-
[docs]@patch("evennia.commands.account.COMMAND_DEFAULT_CLASS",MuxCommand)
-@patch("evennia.commands.admin.COMMAND_DEFAULT_CLASS",MuxCommand)
-@patch("evennia.commands.batchprocess.COMMAND_DEFAULT_CLASS",MuxCommand)
-@patch("evennia.commands.building.COMMAND_DEFAULT_CLASS",MuxCommand)
-@patch("evennia.commands.comms.COMMAND_DEFAULT_CLASS",MuxCommand)
-@patch("evennia.commands.general.COMMAND_DEFAULT_CLASS",MuxCommand)
-@patch("evennia.commands.help.COMMAND_DEFAULT_CLASS",MuxCommand)
-@patch("evennia.commands.syscommands.COMMAND_DEFAULT_CLASS",MuxCommand)
-@patch("evennia.commands.system.COMMAND_DEFAULT_CLASS",MuxCommand)
-@patch("evennia.commands.unloggedin.COMMAND_DEFAULT_CLASS",MuxCommand)
-classBaseEvenniaCommandTest(BaseEvenniaTest,EvenniaCommandTestMixin):
- """
- Commands only using the default settings.
-
- """
-
-
-
[docs]classEvenniaCommandTest(EvenniaTest,EvenniaCommandTestMixin):
- """
- Parent class to inherit from - makes tests use your own
- classes and settings in mygame.
-
- """
[docs]defpronoun_to_viewpoints(pronoun,options=None,pronoun_type=None,gender=None,viewpoint=None):"""
- Access function for determining the forms of a pronount from different viewpoints.
+ Access function for determining the forms of a pronoun from different viewpoints. Args: pronoun (str): A valid English pronoun, such as 'you', 'his', 'themselves' etc.
@@ -322,13 +316,13 @@
# get the default data for the input pronounsource_viewpoint,source_gender,source_type=PRONOUN_TABLE[pronoun_lower]
- # differentiators
+ # use the source pronoun's attributes as defaultsifpronoun_typenotinPRONOUN_TYPES:
- pronoun_type=DEFAULT_PRONOUN_TYPE
+ pronoun_type=source_type[0]ifis_iter(source_type)elsesource_typeifviewpointnotinVIEWPOINTS:
- viewpoint=DEFAULT_VIEWPOINT
+ viewpoint=source_viewpointifgendernotinGENDERS:
- gender=DEFAULT_GENDER
+ gender=source_gender[0]ifis_iter(source_gender)elsesource_genderifoptions:# option string/list will override the kwargs differentiators given
@@ -357,19 +351,8 @@
else:viewpoint=target_viewpoint
- # special handling for the royal "we"
- ifis_iter(source_gender):
- gender_opts=list(source_gender)
- else:
- gender_opts=[source_gender]
- ifviewpoint=="1st person":
- # make sure plural is always an option when converting to 1st person
- # it doesn't matter if it's in the list twice, so don't bother checking
- gender_opts.append("plural")
- # if the gender is still not in the extended options, fall back to source pronoun's default
- gender=genderifgenderingender_optselsegender_opts[0]
-
- # step down into the mapping
+ # by this point, gender will be a valid option from GENDERS and type/viewpoint will be validated
+ # step down into the mapping to get the converted pronounviewpoint_map=PRONOUN_MAPPING[viewpoint]pronouns=viewpoint_map.get(pronoun_type,viewpoint_map[DEFAULT_PRONOUN_TYPE])mapped_pronoun=pronouns.get(gender,pronouns[DEFAULT_GENDER])
diff --git a/docs/1.0-dev/_modules/evennia/utils/verb_conjugation/tests.html b/docs/1.0-dev/_modules/evennia/utils/verb_conjugation/tests.html
index 52d8cd3f46..492b5f3598 100644
--- a/docs/1.0-dev/_modules/evennia/utils/verb_conjugation/tests.html
+++ b/docs/1.0-dev/_modules/evennia/utils/verb_conjugation/tests.html
@@ -81,9 +81,8 @@
"""
-fromdjango.testimportTestCasefromparameterizedimportparameterized
-
+fromdjango.testimportTestCasefrom.importconjugate,pronouns
@@ -350,33 +349,51 @@
[docs]classTestPronounMapping(TestCase):""" Test pronoun viewpoint mapping
-
"""
+ @parameterized.expand(
+ [
+ ("you","you","it"),# default 3rd is "neutral"
+ ("I","I","it"),
+ ("Me","Me","It"),
+ ("ours","ours","theirs"),
+ ("yourself","yourself","itself"),
+ ("yourselves","yourselves","themselves"),
+ ("he","you","he"),# assume 2nd person
+ ("her","you","her"),
+ ("their","your","their"),
+ ("itself","yourself","itself"),
+ ("herself","yourself","herself"),
+ ("themselves","yourselves","themselves"),
+ ]
+ )
+ deftest_default_mapping(self,pronoun,expected_1st_or_2nd_person,expected_3rd_person):
+ """
+ Test the pronoun mapper.
+
+ """
+ received_1st_or_2nd_person,received_3rd_person=pronouns.pronoun_to_viewpoints(pronoun)
+
+ self.assertEqual(expected_1st_or_2nd_person,received_1st_or_2nd_person)
+ self.assertEqual(expected_3rd_person,received_3rd_person)
+
@parameterized.expand([("you","m","you","he"),("you","f op","you","her"),
- ("I","","I","it"),
- ("I","p","I","it"),# plural is invalid
+ ("you","p op","you","them"),("I","m","I","he"),("Me","n","Me","It"),("your","p","your","their"),
- ("ours","","ours","theirs"),
- ("yourself","","yourself","itself"),("yourself","m","yourself","himself"),("yourself","f","yourself","herself"),
- ("yourself","p","yourself","itself"),# plural is invalid("yourselves","","yourselves","themselves"),
- ("he","","you","he"),# assume 2nd person("he","1","I","he"),
- ("he","1 p","we","he"),
+ ("he","1 p","we","he"),# royal we
+ ("we","m","we","he"),# royal we, other way("her","p","you","her"),("her","pa","your","her"),
- ("their","pa","your","their"),
- ("itself","","yourself","itself"),
- ("themselves","","yourselves","themselves"),
- ("herself","","yourself","herself"),
+ ("their","ma","your","their"),])deftest_mapping_with_options(
@@ -389,6 +406,33 @@
received_1st_or_2nd_person,received_3rd_person=pronouns.pronoun_to_viewpoints(pronoun,options)
+ self.assertEqual(expected_1st_or_2nd_person,received_1st_or_2nd_person)
+ self.assertEqual(expected_3rd_person,received_3rd_person)
+
+ @parameterized.expand(
+ [
+ ("you","p","you","they"),
+ ("I","p","I","they"),
+ ("Me","p","Me","Them"),
+ ("your","p","your","their"),
+ ("they","1 p","we","they"),
+ ("they","","you","they"),
+ ("yourself","p","yourself","themselves"),
+ ("myself","p","myself","themselves"),
+ ]
+ )
+ deftest_colloquial_plurals(
+ self,pronoun,options,expected_1st_or_2nd_person,expected_3rd_person
+ ):
+ """
+ The use of this module by the funcparser expects a default person-pronoun
+ of the neutral "they", which is categorized here by the plural.
+
+ """
+ received_1st_or_2nd_person,received_3rd_person=pronouns.pronoun_to_viewpoints(
+ pronoun,options
+ )
+
self.assertEqual(expected_1st_or_2nd_person,received_1st_or_2nd_person)self.assertEqual(expected_3rd_person,received_3rd_person)
[docs]deftest_create(self):
- views=self.get_view_details("list")
- forviewinviews:
- withself.subTest(msg=f"Testing {view.view_name} create"):
- # create is a POST request off of <type>-list
- view_url=reverse(f"api:{view.view_name}")
- # check failures from not sending required fields
- response=self.client.post(view_url)
- self.assertEqual(response.status_code,400)
- # check success when sending the required data
- response=self.client.post(view_url,data=view.create_data)
- self.assertEqual(response.status_code,201,f"Response was {response.data}")
-
-
[docs]deftest_set_attribute(self):
- views=self.get_view_details("set-attribute")
- forviewinviews:
- withself.subTest(msg=f"Testing {view.view_name}"):
- view_url=reverse(f"api:{view.view_name}",kwargs={"pk":view.obj.pk})
- # check failures from not sending required fields
- response=self.client.post(view_url)
- self.assertEqual(response.status_code,400,f"Response was: {response.data}")
- # test adding an attribute
- self.assertEqual(view.obj.db.some_test_attr,None)
- attr_name="some_test_attr"
- attr_data={"db_key":attr_name,"db_value":"test_value"}
- response=self.client.post(view_url,data=attr_data)
- self.assertEqual(response.status_code,200,f"Response was: {response.data}")
- self.assertEquals(view.obj.attributes.get(attr_name),"test_value")
- # now test removing it
- attr_data={"db_key":attr_name}
- response=self.client.post(view_url,data=attr_data)
- self.assertEqual(response.status_code,200,f"Response was: {response.data}")
- self.assertEquals(view.obj.attributes.get(attr_name),None)
[docs]deftest_get(self):
- # Try accessing page while not logged in
- response=self.client.get(reverse(self.url_name,kwargs=self.get_kwargs()))
- self.assertEqual(response.status_code,self.unauthenticated_response)
[docs]defsetUp(self):
- super().setUp()
-
- # create a db help entry
- create_help_entry("unit test db entry","unit test db entry text",category="General")
-
-
[docs]defget_kwargs(self):
- return{"category":slugify("general"),"topic":slugify("unit test db entry")}
-
-
[docs]deftest_view(self):
- response=self.client.get(reverse(self.url_name,kwargs=self.get_kwargs()),follow=True)
- self.assertEqual(response.context["entry_text"],"unit test db entry text")
-
-
[docs]deftest_object_cache(self):
- # clear file help entries, use local HELP_ENTRY_DICTS to recreate new entries
- global_FILE_HELP_ENTRIES
- if_FILE_HELP_ENTRIESisNone:
- fromevennia.help.filehelpimportFILE_HELP_ENTRIESas_FILE_HELP_ENTRIES
- help_module="evennia.web.website.tests"
- self.file_help_store=_FILE_HELP_ENTRIES.__init__(help_file_modules=[help_module])
-
- # request access to an entry
- response=self.client.get(reverse(self.url_name,kwargs=self.get_kwargs()),follow=True)
- self.assertEqual(response.context["entry_text"],"unit test db entry text")
- # request a second entry, verifing the cached object is not provided on a new topic request
- entry_two_args={"category":slugify("general"),"topic":slugify("unit test file entry")}
- response=self.client.get(reverse(self.url_name,kwargs=entry_two_args),follow=True)
- self.assertEqual(response.context["entry_text"],"cache test file entry text")
[docs]defsetUp(self):
- super().setUp()
-
- # create a db entry with a lock
- self.db_help_entry=create_help_entry(
- "unit test locked topic",
- "unit test locked entrytext",
- category="General",
- locks="read:perm(Developer)",
- )
-
-
[docs]defget_kwargs(self):
- return{"category":slugify("general"),"topic":slugify("unit test locked topic")}
-
-
[docs]deftest_locked_entry(self):
- # request access to an entry for permission the account does not have
- response=self.client.get(reverse(self.url_name,kwargs=self.get_kwargs()),follow=True)
- self.assertEqual(response.context["entry_text"],"Failed to find entry.")
-
-
[docs]deftest_lock_with_perm(self):
- # log TestAccount in, grant permission required, read the entry
- self.login()
- self.account.permissions.add("Developer")
- response=self.client.get(reverse(self.url_name,kwargs=self.get_kwargs()),follow=True)
- self.assertEqual(response.context["entry_text"],"unit test locked entrytext")
[docs]@override_settings(MAX_NR_CHARACTERS=1)
- deftest_valid_access_multisession_0(self):
- "Account1 with no characters should be able to create a new one"
- self.account.db._playable_characters=[]
-
- # Login account
- self.login()
-
- # Post data for a new character
- data={"db_key":"gannon","desc":"Some dude."}
-
- response=self.client.post(reverse(self.url_name),data=data,follow=True)
- self.assertEqual(response.status_code,200)
-
- # Make sure the character was actually created
- self.assertTrue(
- len(self.account.db._playable_characters)==1,
- "Account only has the following characters attributed to it: %s"
- %self.account.db._playable_characters,
- )
-
-
[docs]@override_settings(MAX_NR_CHARACTERS=5)
- deftest_valid_access_multisession_2(self):
- "Account1 should be able to create multiple new characters"
- # Login account
- self.login()
-
- # Post data for a new character
- data={"db_key":"gannon","desc":"Some dude."}
-
- response=self.client.post(reverse(self.url_name),data=data,follow=True)
- self.assertEqual(response.status_code,200)
-
- # Make sure the character was actually created
- self.assertTrue(
- len(self.account.db._playable_characters)>1,
- "Account only has the following characters attributed to it: %s"
- %self.account.db._playable_characters,
- )
[docs]deftest_invalid_access(self):
- "Account1 should not be able to puppet Account2:Char2"
- # Login account
- self.login()
-
- # Try to access puppet page for char2
- kwargs={"pk":self.char2.pk,"slug":slugify(self.char2.name)}
- response=self.client.get(reverse(self.url_name,kwargs=kwargs),follow=True)
- self.assertTrue(
- response.status_code>=400,
- "Invalid access should return a 4xx code-- either obj not found or permission denied!"
- " (Returned %s)"%response.status_code,
- )
[docs]deftest_valid_access(self):
- "Account1 should be able to update Account1:Char1"
- # Login account
- self.login()
-
- # Try to access update page for char1
- response=self.client.get(reverse(self.url_name,kwargs=self.get_kwargs()),follow=True)
- self.assertEqual(response.status_code,200)
-
- # Try to update char1 desc
- data={"db_key":self.char1.db_key,"desc":"Just a regular type of dude."}
- response=self.client.post(
- reverse(self.url_name,kwargs=self.get_kwargs()),data=data,follow=True
- )
- self.assertEqual(response.status_code,200)
-
- # Make sure the change was made successfully
- self.assertEqual(self.char1.db.desc,data["desc"])
-
-
[docs]deftest_invalid_access(self):
- "Account1 should not be able to update Account2:Char2"
- # Login account
- self.login()
-
- # Try to access update page for char2
- kwargs={"pk":self.char2.pk,"slug":slugify(self.char2.name)}
- response=self.client.get(reverse(self.url_name,kwargs=kwargs),follow=True)
- self.assertEqual(response.status_code,403)
[docs]deftest_valid_access(self):
- "Account1 should be able to delete Account1:Char1"
- # Login account
- self.login()
-
- # Try to access delete page for char1
- response=self.client.get(reverse(self.url_name,kwargs=self.get_kwargs()),follow=True)
- self.assertEqual(response.status_code,200)
-
- # Proceed with deleting it
- data={"value":"yes"}
- response=self.client.post(
- reverse(self.url_name,kwargs=self.get_kwargs()),data=data,follow=True
- )
- self.assertEqual(response.status_code,200)
-
- # Make sure it deleted
- self.assertFalse(
- self.char1inself.account.db._playable_characters,
- "Char1 is still in Account playable characters list.",
- )
-
-
[docs]deftest_invalid_access(self):
- "Account1 should not be able to delete Account2:Char2"
- # Login account
- self.login()
-
- # Try to access delete page for char2
- kwargs={"pk":self.char2.pk,"slug":slugify(self.char2.name)}
- response=self.client.get(reverse(self.url_name,kwargs=kwargs),follow=True)
- self.assertEqual(response.status_code,403)
-
-
-
\ No newline at end of file
diff --git a/docs/1.0-dev/_modules/index.html b/docs/1.0-dev/_modules/index.html
index 0d76f81670..90d1798e77 100644
--- a/docs/1.0-dev/_modules/index.html
+++ b/docs/1.0-dev/_modules/index.html
@@ -104,34 +104,25 @@
-
-
-
\ No newline at end of file
diff --git a/docs/1.0-dev/_sources/Coding/Changelog.md.txt b/docs/1.0-dev/_sources/Coding/Changelog.md.txt
index 7ce14f5bfa..c0cca561fb 100644
--- a/docs/1.0-dev/_sources/Coding/Changelog.md.txt
+++ b/docs/1.0-dev/_sources/Coding/Changelog.md.txt
@@ -5,7 +5,7 @@
> Not released yet
> 2019-2022 develop branch (WIP)
-Increase requirements: Django 4.1+, Twisted 22+ Python 3.9, 3.10, 3.11. PostgreSQL 11+.
+Increase requirements: Django 4.1+, Twisted 22.10+ Python 3.9, 3.10, 3.11. PostgreSQL 11+.
- New `drop:holds()` lock default to limit dropping nonsensical things. Access check
defaults to True for backwards-compatibility in 0.9, will be False in 1.0
diff --git a/docs/1.0-dev/_sources/Setup/Installation-Android.md.txt b/docs/1.0-dev/_sources/Setup/Installation-Android.md.txt
index 737de0e810..e49b6eeea5 100644
--- a/docs/1.0-dev/_sources/Setup/Installation-Android.md.txt
+++ b/docs/1.0-dev/_sources/Setup/Installation-Android.md.txt
@@ -1,9 +1,7 @@
# Installing on Android
-This page describes how to install and run the Evennia server on an Android phone. This will involve
-installing a slew of third-party programs from the Google Play store, so make sure you are okay with
-this before starting.
+This page describes how to install and run the Evennia server on an Android phone. This will involve installing a slew of third-party programs from the Google Play store, so make sure you are okay with this before starting.
```{warning}
Android installation is experimental and not tested with later versions of Android.
@@ -12,23 +10,16 @@ Report your findings.
## Install Termux
-The first thing to do is install a terminal emulator that allows a "full" version of linux to be
-run. Note that Android is essentially running on top of linux so if you have a rooted phone, you may
-be able to skip this step. You *don't* require a rooted phone to install Evennia though.
+The first thing to do is install a terminal emulator that allows a "full" version of linux to be run. Note that Android is essentially running on top of linux so if you have a rooted phone, you may be able to skip this step. You *don't* require a rooted phone to install Evennia though.
-Assuming we do not have root, we will install
-[Termux](https://play.google.com/store/apps/details?id=com.termux&hl=en).
-Termux provides a base installation of Linux essentials, including apt and Python, and makes them
-available under a writeable directory. It also gives us a terminal where we can enter commands. By
-default, Android doesn't give you permissions to the root folder, so Termux pretends that its own
-installation directory is the root directory.
+Assuming we do not have root, we will install [Termux](https://play.google.com/store/apps/details?id=com.termux&hl=en). Termux provides a base installation of Linux essentials, including apt and Python, and makes them available under a writeable directory. It also gives us a terminal where we can enter commands. By default, Android doesn't give you permissions to the root folder, so Termux pretends that its own installation directory is the root directory.
-Termux will set up a base system for us on first launch, but we will need to install some
-prerequisites for Evennia. Commands you should run in Termux will look like this:
+Termux will set up a base system for us on first launch, but we will need to install some prerequisites for Evennia. Commands you should run in Termux will look like this:
```
$ cat file.txt
```
+
The `$` symbol is your prompt - do not include it when running commands.
## Prerequisites
diff --git a/docs/1.0-dev/_sources/Setup/Installation-Docker.md.txt b/docs/1.0-dev/_sources/Setup/Installation-Docker.md.txt
index 5ba1597427..8ad0ca4861 100644
--- a/docs/1.0-dev/_sources/Setup/Installation-Docker.md.txt
+++ b/docs/1.0-dev/_sources/Setup/Installation-Docker.md.txt
@@ -40,57 +40,30 @@ You can run Evennia from inside this container if you want to, it's like you are
isolated Linux environment. To exit the container and all processes in there, press `Ctrl-D`. If you
created a new game folder, you will find that it has appeared on-disk.
-> The game folder or any new files that you created from inside the container will appear as owned
-by `root`. If you want to edit the files outside of the container you should change the ownership.
-On Linux/Mac you do this with `sudo chown myname:myname -R mygame`, where you replace `myname` with
-your username and `mygame` with whatever your game folder is named.
+> The game folder or any new files that you created from inside the container will appear as owned by `root`. If you want to edit the files outside of the container you should change the ownership. On Linux/Mac you do this with `sudo chown myname:myname -R mygame`, where you replace `myname` with your username and `mygame` with whatever your game folder is named.
Below is an explanation of the `docker run` command we used:
-- `docker run ... evennia/evennia` tells us that we want to run a new container based on the
-`evennia/evennia` docker image. Everything in between are options for this. The `evennia/evennia` is
-the name of our [official docker image on the dockerhub
-repository](https://hub.docker.com/r/evennia/evennia/). If you didn't do `docker pull
-evennia/evennia` first, the image will be downloaded when running this, otherwise your already
-downloaded version will be used. It contains everything needed to run Evennia.
+- `docker run ... evennia/evennia` tells us that we want to run a new container based on the `evennia/evennia` docker image. Everything in between are options for this. The `evennia/evennia` is the name of our [official docker image on the dockerhub repository](https://hub.docker.com/r/evennia/evennia/). If you didn't do `docker pull evennia/evennia` first, the image will be downloaded when running this, otherwise your already downloaded version will be used. It contains everything needed to run Evennia.
- `-it` has to do with creating an interactive session inside the container we start.
- `--rm` will make sure to delete the container when it shuts down. This is nice to keep things tidy
on your drive.
-- `-p 4000:4000 -p 4001:4001 -p 4002:4002` means that we *map* ports `4000`, `4001` and `4002` from
-inside the docker container to same-numbered ports on our host machine. These are ports for telnet,
-webserver and websockets. This is what allows your Evennia server to be accessed from outside the
-container (such as by your MUD client)!
-- `-v $PWD:/usr/src/game` mounts the current directory (*outside* the container) to the path
-`/usr/src/game` *inside* the container. This means that when you edit that path in the container you
-will actually be modifying the "real" place on your hard drive. If you didn't do this, any changes
-would only exist inside the container and be gone if we create a new one. Note that in linux a
-shortcut for the current directory is `$PWD`. If you don't have this for your OS, you can replace it
-with the full path to the current on-disk directory (like `C:/Development/evennia/game` or wherever
-you want your evennia files to appear).
-- `--user $UID:$GID` ensures the container's modifications to `$PWD` are done with you user and
-group IDs instead of root's IDs (root is the user running evennia inside the container). This avoids
-having stale `.pid` files in your filesystem between container reboots which you have to force
-delete with `sudo rm server/*.pid` before each boot.
+- `-p 4000:4000 -p 4001:4001 -p 4002:4002` means that we *map* ports `4000`, `4001` and `4002` from inside the docker container to same-numbered ports on our host machine. These are ports for telnet, webserver and websockets. This is what allows your Evennia server to be accessed from outside the container (such as by your MUD client)!
+- `-v $PWD:/usr/src/game` mounts the current directory (*outside* the container) to the path `/usr/src/game` *inside* the container. This means that when you edit that path in the container you will actually be modifying the "real" place on your hard drive. If you didn't do this, any changes would only exist inside the container and be gone if we create a new one. Note that in linux a shortcut for the current directory is `$PWD`. If you don't have this for your OS, you can replace it with the full path to the current on-disk directory (like `C:/Development/evennia/game` or wherever you want your evennia files to appear).
+- `--user $UID:$GID` ensures the container's modifications to `$PWD` are done with you user and group IDs instead of root's IDs (root is the user running evennia inside the container). This avoids having stale `.pid` files in your filesystem between container reboots which you have to force delete with `sudo rm server/*.pid` before each boot.
## Running your game as a docker image
If you run the `docker` command given in the previous section from your game dir you can then
easily start Evennia and have a running server without any further fuss.
-But apart from ease of install, the primary benefit to running an Evennia-based game in a container
-is to simplify its deployment into a public production environment. Most cloud-based hosting
+But apart from ease of install, the primary benefit to running an Evennia-based game in a container is to simplify its deployment into a public production environment. Most cloud-based hosting
providers these days support the ability to run container-based applications. This makes deploying
-or updating your game as simple as building a new container image locally, pushing it to your Docker
-Hub account, and then pulling from Docker Hub into your AWS/Azure/other docker-enabled hosting
-account. The container eliminates the need to install Python, set up a virtualenv, or run pip to
-install dependencies.
+or updating your game as simple as building a new container image locally, pushing it to your Docker Hub account, and then pulling from Docker Hub into your AWS/Azure/other docker-enabled hosting account. The container eliminates the need to install Python, set up a virtualenv, or run pip to install dependencies.
### Start Evennia and run through docker
-For remote or automated deployment you may want to start Evennia immediately as soon as the docker
-container comes up. If you already have a game folder with a database set up you can also start the
-docker container and pass commands directly to it. The command you pass will be the main process to
-run in the container. From your game dir, run for example this command:
+For remote or automated deployment you may want to start Evennia immediately as soon as the docker container comes up. If you already have a game folder with a database set up you can also start the docker container and pass commands directly to it. The command you pass will be the main process to run in the container. From your game dir, run for example this command:
docker run -it --rm -p 4000:4000 -p 4001:4001 -p 4002:4002 --rm -v $PWD:/usr/src/game evennia/evennia evennia start -l
@@ -101,9 +74,7 @@ and the container go down.
## Create your own game image
-These steps assume that you have created or otherwise obtained a game directory already. First, `cd`
-to your game dir and create a new empty text file named `Dockerfile`. Save the following two lines
-into it:
+These steps assume that you have created or otherwise obtained a game directory already. First, `cd` to your game dir and create a new empty text file named `Dockerfile`. Save the following two lines into it:
```
FROM evennia/evennia:latest
@@ -122,9 +93,7 @@ To build the image:
```
(don't forget the period at the end, it will use the `Dockerfile` from the current location). Here
-`mydhaccount` is the name of your `dockerhub` account. If you don't have a dockerhub account you can
-build the image locally only (name the container whatever you like in that case, like just
-`mygame`).
+`mydhaccount` is the name of your `dockerhub` account. If you don't have a dockerhub account you can build the image locally only (name the container whatever you like in that case, like just `mygame`).
Docker images are stored centrally on your computer. You can see which ones you have available
locally with `docker images`. Once built, you have a couple of options to run your game.
@@ -153,9 +122,7 @@ option and just give the following command:
docker run -it --rm -d -p 4000:4000 -p 4001:4001 -p 4002:4002 --user $UID:$GID mydhaccount/mygame
```
-Your game will be downloaded from your docker-hub account and a new container will be built using
-the image and started on the server! If your server environment forces you to use different ports,
-you can just map the normal ports differently in the command above.
+Your game will be downloaded from your docker-hub account and a new container will be built using the image and started on the server! If your server environment forces you to use different ports, you can just map the normal ports differently in the command above.
Above we added the `-d` option, which starts the container in *daemon* mode - you won't see any
return in the console. You can see it running with `docker ps`:
@@ -193,26 +160,20 @@ container will get a new container id to reference.
## How it Works
-The `evennia/evennia` docker image holds the evennia library and all of its dependencies. It also
-has an `ONBUILD` directive which is triggered during builds of images derived from it. This
-`ONBUILD` directive handles setting up a volume and copying your game directory code into the proper
-location within the container.
+The `evennia/evennia` docker image holds the evennia library and all of its dependencies. It also has an `ONBUILD` directive which is triggered during builds of images derived from it. This
+`ONBUILD` directive handles setting up a volume and copying your game directory code into the proper location within the container.
-In most cases, the Dockerfile for an Evennia-based game will only need the `FROM
-evennia/evennia:latest` directive, and optionally a `MAINTAINER` directive if you plan to publish
+In most cases, the Dockerfile for an Evennia-based game will only need the `FROM evennia/evennia:latest` directive, and optionally a `MAINTAINER` directive if you plan to publish
your image on Docker Hub and would like to provide contact info.
-For more information on Dockerfile directives, see the [Dockerfile
-Reference](https://docs.docker.com/engine/reference/builder/).
+For more information on Dockerfile directives, see the [Dockerfile Reference](https://docs.docker.com/engine/reference/builder/).
For more information on volumes and Docker containers, see the Docker site's [Manage data in
containers](https://docs.docker.com/engine/tutorials/dockervolumes/) page.
### What if I Don't Want "LATEST"?
-A new `evennia/evennia` image is built automatically whenever there is a new commit to the `master`
-branch of Evennia. It is possible to create your own custom evennia base docker image based on any
-arbitrary commit.
+A new `evennia/evennia` image is built automatically whenever there is a new commit to the `master` branch of Evennia. It is possible to create your own custom evennia base docker image based on any arbitrary commit.
1. Use git tools to checkout the commit that you want to base your image upon. (In the example
below, we're checking out commit a8oc3d5b.)
@@ -236,9 +197,7 @@ to be:
FROM mydhacct/evennia:latest
```
-Note: From this point, you can also use the `docker tag` command to set a specific tag on your image
-and/or upload it into Docker Hub under your account.
-
+Note: From this point, you can also use the `docker tag` command to set a specific tag on your image and/or upload it into Docker Hub under your account.
5. At this point, build your game using the same `docker build` command as usual. Change your
working directory to be your game directory and run
@@ -248,10 +207,7 @@ docker build -t mydhaccountt/mygame .
## Additional Creature Comforts
-The Docker ecosystem includes a tool called `docker-compose`, which can orchestrate complex multi-
-container applications, or in our case, store the default port and terminal parameters that we want
-specified every time we run our container. A sample `docker-compose.yml` file to run a containerized
-Evennia game in development might look like this:
+The Docker ecosystem includes a tool called `docker-compose`, which can orchestrate complex multi- container applications, or in our case, store the default port and terminal parameters that we want specified every time we run our container. A sample `docker-compose.yml` file to run a containerized Evennia game in development might look like this:
```
version: '2'
@@ -274,8 +230,4 @@ docker-compose up
For more information about `docker-compose`, see [Getting Started with docker-
compose](https://docs.docker.com/compose/gettingstarted/).
-> Note that with this setup you lose the `--user $UID` option. The problem is that the variable
-`UID` is not available inside the configuration file `docker-compose.yml`. A workaround is to
-hardcode your user and group id. In a terminal run `echo $UID:$GID` and if for example you get
-`1000:1000` you can add to `docker-compose.yml` a line `user: 1000:1000` just below the `image: ...`
-line.
\ No newline at end of file
+> Note that with this setup you lose the `--user $UID` option. The problem is that the variable `UID` is not available inside the configuration file `docker-compose.yml`. A workaround is to hardcode your user and group id. In a terminal run `echo $UID:$GID` and if for example you get `1000:1000` you can add to `docker-compose.yml` a line `user: 1000:1000` just below the `image: ...` line.
\ No newline at end of file
diff --git a/docs/1.0-dev/_sources/Setup/Installation-Git.md.txt b/docs/1.0-dev/_sources/Setup/Installation-Git.md.txt
index 383c03abc8..542c58d925 100644
--- a/docs/1.0-dev/_sources/Setup/Installation-Git.md.txt
+++ b/docs/1.0-dev/_sources/Setup/Installation-Git.md.txt
@@ -35,6 +35,28 @@ Evennia should now be running and you can connect to it by pointing a web browse
`http://localhost:4001` or a MUD telnet client to `localhost:4000` (use `127.0.0.1` if your OS does
not recognize `localhost`).
+## Virtualenv
+
+A Python [virtual environment](https://docs.python.org/3/library/venv.html) allows you to install Evennia in its own little folder, separate from the rest of the system. You also won't need any extra permissions. It's optional to use a virtualenv, but it's highly recommended. Python supports this natively:
+
+ python3.11 -m venv evenv
+
+This will create a new folder `evenv` in your current directory.
+Activate it like this:
+
+`source evenv/bin/activate` (Linux, Mac)
+`evenv\Scripts\activate` (Windows)
+`.\evenv\scripts\activate` (Windows with PS Shell, Git Bash etc)
+
+The text `(evenv)` should appear next to your prompt to show that the virtual
+environment is active. You _don't_ need to actually be in or near the `evenv` folder for
+the environment to be active.
+
+> Remember that you need to re-activate the virtualenv like this *every time* you
+> start a new terminal/console to get access to the Python packages (notably the
+> important `evennia` program) you installed in the virtualenv!
+
+
## Linux Install
For Debian-derived systems (like Ubuntu, Mint etc), start a terminal and
@@ -59,42 +81,18 @@ Next we fetch Evennia itself:
git clone https://github.com/evennia/evennia.git
```
A new folder `evennia` will appear containing the Evennia library. This only
-contains the source code though, it is not *installed* yet. To isolate the
-Evennia install and its dependencies from the rest of the system, it is good
-Python practice to install into a _virtualenv_. If you are unsure about what a
-virtualenv is and why it's useful, see the [Glossary entry on
-virtualenv](../Glossary.md#virtualenv).
+contains the source code though, it is not *installed* yet.
-```
-python3.10 -m venv evenv
-```
+At this point it's now optional but recommended that you initialize and activate a [virtualenv](#virtualenv).
-A new folder `evenv` will appear (we could have called it anything). This
-folder will hold a self-contained setup of Python packages without interfering
-with default Python packages on your system (or the Linux distro lagging behind
-on Python package versions). It will also always use the right version of Python.
-Activate the virtualenv:
-
-```
-source evenv/bin/activate
-```
-
-The text `(evenv)` should appear next to your prompt to show that the virtual
-environment is active.
-
-> Remember that you need to activate the virtualenv like this *every time* you
-> start a new terminal to get access to the Python packages (notably the
-> important `evennia` program) we are about to install.
-
-Next, install Evennia into your active virtualenv. Make sure you are standing
-at the top of your mud directory tree (so you see the `evennia/` and `evenv/`
-folders) and run
+Next, install Evennia (system-wide, or into your active virtualenv). Make sure you are standing
+at the top of your mud directory tree (so you see the `evennia/` folder, and likely the `evenv` virtualenv folder) and do
```
pip install -e evennia
```
-Test that you can run the `evennia` command everywhere while your virtualenv (evenv) is active.
+Test that you can run the `evennia` command.
Next you can continue initializing your game from the regular [Installation instructions](./Installation.md).
@@ -107,9 +105,8 @@ terminal](https://blog.teamtreehouse.com/introduction-to-the-mac-os-x-command-li
if you are unsure how it works.
* Python should already be installed but you must make sure it's a high enough version - go for
- 3.10.
-([This](https://docs.python-guide.org/en/latest/starting/install/osx/) discusses
- how you may upgrade it).
+ 3.11.
+([This](https://docs.python-guide.org/en/latest/starting/install/osx/) discusses how you may upgrade it).
* GIT can be obtained with
[git-osx-installer](https://code.google.com/p/git-osx-installer/) or via MacPorts [as described
here](https://git-scm.com/book/en/Getting-Started-Installing-Git#Installing-on-Mac).
@@ -132,32 +129,13 @@ git clone https://github.com/evennia/evennia.git
```
A new folder `evennia` will appear containing the Evennia library. This only
-contains the source code though, it is not *installed* yet. To isolate the
-Evennia install and its dependencies from the rest of the system, it is good
-Python practice to install into a _virtualenv_. If you are unsure about what a
-virtualenv is and why it's useful, see the [Glossary entry on virtualenv](../Glossary.md#virtualenv).
+contains the source code though, it is not *installed* yet.
-```
-python3.10 -m venv evenv
-```
-A new folder `evenv` will appear (we could have called it anything). This
-folder will hold a self-contained setup of Python packages without interfering
-with default Python packages on your system. Activate the virtualenv:
+At this point it's now optional but recommended that you initialize and activate a [virtualenv](#virtualenv).
-```
-source evenv/bin/activate
-```
-
-The text `(evenv)` should appear next to your prompt to show the virtual
-environment is active.
-
-> Remember that you need to activate the virtualenv like this *every time* you
-> start a new terminal to get access to the Python packages (notably the
-> important `evennia` program) we are about to install.
-
-Next, install Evennia into your active virtualenv. Make sure you are standing
-at the top of your mud directory tree (so you see the `evennia/` and `evenv/`
-folders) and run
+Next, install Evennia (system-wide, or into your active virtualenv). Make sure you are standing
+at the top of your mud directory tree (so you see the `evennia/`, and likely the `evenv` virtualenv
+folder) and do
```
pip install --upgrade pip # Old pip versions may be an issue on Mac.
@@ -165,42 +143,29 @@ pip install --upgrade setuptools # Ditto concerning Mac issues.
pip install -e evennia
```
-Test that you can run the `evennia` command everywhere while your virtualenv (evenv) is active.
+Test that you can run the `evennia` command.
Next you can continue initializing your game from the regular [Installation instructions](./Installation.md).
## Windows Install
> If you are running Windows10, consider using the _Windows Subsystem for Linux_
-> ([WSL](https://en.wikipedia.org/wiki/Windows_Subsystem_for_Linux)) instead.
-> Just set up WSL with an Ubuntu image and follow the Linux install instructions above.
+> ([WSL](https://en.wikipedia.org/wiki/Windows_Subsystem_for_Linux)) instead. Just set up WSL with an Ubuntu image and follow the Linux install instructions above.
The Evennia server itself is a command line program. In the Windows launch
menu, start *All Programs -> Accessories -> command prompt* and you will get
-the Windows command line interface. Here is [one of many tutorials on using the Windows command
-line](https://www.bleepingcomputer.com/tutorials/windows-command-prompt-introduction/)
-if you are unfamiliar with it.
+the Windows command line interface. Here is [one of many tutorials on using the Windows command line](https://www.bleepingcomputer.com/tutorials/windows-command-prompt-introduction/) if you are unfamiliar with it.
-* Install Python [from the Python homepage](https://www.python.org/downloads/windows/). You will
-need to be a
-Windows Administrator to install packages. Get Python any version of Python **3.10**, usually
+* Install Python [from the Python homepage](https://www.python.org/downloads/windows/). You will need to be a
+Windows Administrator to install packages. Get Python **3.11**, usually
the 64-bit version (although it doesn't matter too much). **When installing, make sure
to check-mark *all* install options, especially the one about making Python
available on the path (you may have to scroll to see it)**. This allows you to
-just write `python` in any console without first finding where the `python`
+just write `python` (or possibly `py`) in any console without first finding where the `python`
program actually sits on your hard drive.
-* You need to also get [GIT](https://git-scm.com/downloads) and install it. You
-can use the default install options but when you get asked to "Adjust your PATH
-environment", you should select the second option "Use Git from the Windows
-Command Prompt", which gives you more freedom as to where you can use the
-program.
-* Finally you must install the [Microsoft Visual C++ compiler for
-Python](https://aka.ms/vs/16/release/vs_buildtools.exe). Download and run the linked installer and
-install the C++ tools. Keep all the defaults. Allow the install of the "Win10 SDK", even if you are
-on Win7 (not tested on older Windows versions). If you later have issues with installing Evennia due
-to a failure to build the "Twisted wheels", this is where you are missing things.
-* You *may* need the [pypiwin32](https://pypi.python.org/pypi/pypiwin32) Python headers. Install
-these only if you have issues.
+* You need to also get [GIT](https://git-scm.com/downloads) and install it. You can use the default install options but when you get asked to "Adjust your PATH environment", you should select the second option "Use Git from the Windows Command Prompt", which gives you more freedom as to where you can use the program.
+* Finally you must install the [Microsoft Visual C++ compiler for Python](https://aka.ms/vs/16/release/vs_buildtools.exe). Download and run the linked installer and install the C++ tools. Keep all the defaults. Allow the install of the "Win10 SDK", even if you are on Win7 (not tested on older Windows versions). If you later have issues with installing Evennia due to a failure to build the "Twisted wheels", this is where you are missing things.
+* You *may* need the [pypiwin32](https://pypi.python.org/pypi/pypiwin32) Python headers. Install these only if you have issues.
You can install Evennia wherever you want. `cd` to that location and create a
new folder for all your Evennia development (let's call it `muddev`).
@@ -220,35 +185,12 @@ git clone https://github.com/evennia/evennia.git
```
A new folder `evennia` will appear containing the Evennia library. This only
-contains the source code though, it is not *installed* yet. To isolate the
-Evennia install and its dependencies from the rest of the system, it is good
-Python practice to install into a _virtualenv_. If you are unsure about what a
-virtualenv is and why it's useful, see the [Glossary entry on virtualenv](../Glossary.md#virtualenv).
+contains the source code though, it is not *installed* yet.
-```
-python3.10 -m venv evenv
-```
-A new folder `evenv` will appear (we could have called it anything). This
-folder will hold a self-contained setup of Python packages without interfering
-with default Python packages on your system. Activate the virtualenv:
+At this point it's optional but recommended that you initialize and activate a [virtualenv](#virtualenv).
-```
-# If you are using a standard command prompt, you can use the following:
-evenv\scripts\activate.bat
-
-# If you are using a PS Shell, Git Bash, or other, you can use the following:
-.\evenv\scripts\activate
-```
-The text `(evenv)` should appear next to your prompt to show the virtual
-environment is active.
-
-> Remember that you need to activate the virtualenv like this *every time* you
-> start a new console window if you want to get access to the Python packages
-> (notably the important `evennia` program) we are about to install.
-
-Next, install Evennia into your active virtualenv. Make sure you are standing
-at the top of your mud directory tree (so you see the `evennia` and `evenv`
-folders when you use the `dir` command) and run
+Next, install Evennia (system wide, or into the virtualenv). Make sure you are standing
+at the top of your mud directory tree (so you see `evennia`, and likely the `evenv` virtualenv folder when running the `dir` command). Then do:
```
pip install -e evennia
diff --git a/docs/1.0-dev/_sources/Setup/Installation-Troubleshooting.md.txt b/docs/1.0-dev/_sources/Setup/Installation-Troubleshooting.md.txt
index 24be9c0a81..902a80603b 100644
--- a/docs/1.0-dev/_sources/Setup/Installation-Troubleshooting.md.txt
+++ b/docs/1.0-dev/_sources/Setup/Installation-Troubleshooting.md.txt
@@ -14,26 +14,19 @@ everything in the following sections.
- Windows (Win7, Win8, Win10, Win11)
- Mac OSX (>10.5 recommended)
-- [Python](https://www.python.org) (v3.9 and 3.10 are tested)
- - [virtualenv](https://pypi.python.org/pypi/virtualenv) for making isolated
- Python environments. Installed with `pip install virtualenv`.
+- [Python](https://www.python.org) (v3.9, 3.10 and 3.11 are tested)
- [Twisted](https://twistedmatrix.com) (v22.0+)
- - [ZopeInterface](https://www.zope.org/Products/ZopeInterface) (v3.0+) - usually included in
- Twisted packages
+ - [ZopeInterface](https://www.zope.org/Products/ZopeInterface) (v3.0+) - usually included in Twisted packages
- Linux/Mac users may need the `gcc` and `python-dev` packages or equivalent.
- - Windows users need [MS Visual C++](https://aka.ms/vs/16/release/vs_buildtools.exe) and *maybe*
- [pypiwin32](https://pypi.python.org/pypi/pypiwin32).
-- [Django](https://www.djangoproject.com) (v4.0.1+), be warned that latest dev
- version is usually untested with Evennia.
+ - Windows users need [MS Visual C++](https://aka.ms/vs/16/release/vs_buildtools.exe) and *maybe* [pypiwin32](https://pypi.python.org/pypi/pypiwin32).
+- [Django](https://www.djangoproject.com) (v4.2+), be warned that latest dev version is usually untested with Evennia.
- [GIT](https://git-scm.com/) - version control software used if you want to install the sources
- (but also useful to track your own code) - Mac users can use the
- [git-osx-installer](https://code.google.com/p/git-osx-installer/) or the
- [MacPorts version](https://git-scm.com/book/en/Getting-Started-Installing-Git#Installing-on-Mac).
+ (but also useful to track your own code)
+ - Mac users can use the [git-osx-installer](https://code.google.com/p/git-osx-installer/) or the [MacPorts version](https://git-scm.com/book/en/Getting-Started-Installing-Git#Installing-on-Mac).
## Confusion of location (GIT installation)
-It's common to be confused and install Evennia in the wrong location. After following the
-[git install](./Installation-Git.md) instructions, the folder structure should look like this:
+When doing the [Git installation](./Installation-Git.md), some may be confused and install Evennia in the wrong location. After following the instructions (and using a virtualenv), the folder structure should look like this:
```
muddev/
@@ -47,15 +40,14 @@ is `mygame/server/conf/settings.py` and the _parent_ setting file is `evennia/ev
## Virtualenv setup fails
-When doing the `python3.10 -m venv evenv` step, some users report getting an error; something like:
+When doing the `python3.11 -m venv evenv` step, some users report getting an error; something like:
Error: Command '['evenv', '-Im', 'ensurepip', '--upgrade', '--default-pip']'
returned non-zero exit status 1
-You can solve this by installing the `python3.10-venv` package or equivalent for your OS. Alternatively
-you can bootstrap it in this way:
+You can solve this by installing the `python3.11-venv` package or equivalent for your OS. Alternatively you can bootstrap it in this way:
- python3.10 -m --without-pip evenv
+ python3.11 -m --without-pip evenv
This should set up the virtualenv without `pip`. Activate the new virtualenv and then install pip from within it:
@@ -74,64 +66,34 @@ If `localhost` doesn't work when trying to connect to your local game, try `127.
## Linux Troubleshooting
- If you get an error when installing Evennia (especially with lines mentioning
- failing to include `Python.h`) then try `sudo apt-get install python3-setuptools python3-dev`.
- Once installed, run `pip install -e evennia` again.
+ failing to include `Python.h`) then try `sudo apt-get install python3-setuptools python3-dev`. Once installed, run `pip install -e evennia` again.
- When doing a [git install](./Installation-Git.md), some not-updated Linux distributions may give errors
about a too-old `setuptools` or missing `functools`. If so, update your environment
with `pip install --upgrade pip wheel setuptools`. Then try `pip install -e evennia` again.
-- One user reported a rare issue on Ubuntu 16 is an install error on installing Twisted; `Command
- "python setup.py egg_info" failed with error code 1 in /tmp/pip-build-vnIFTg/twisted/` with errors
- like `distutils.errors.DistutilsError: Could not find suitable distribution for
- Requirement.parse('incremental>=16.10.1')`. This appears possible to solve by simply updating Ubuntu
- with `sudo apt-get update && sudo apt-get dist-upgrade`.
+- One user reported a rare issue on Ubuntu 16 is an install error on installing Twisted; `Command "python setup.py egg_info" failed with error code 1 in /tmp/pip-build-vnIFTg/twisted/` with errors like `distutils.errors.DistutilsError: Could not find suitable distribution for Requirement.parse('incremental>=16.10.1')`. This appears possible to solve by simply updating Ubuntu with `sudo apt-get update && sudo apt-get dist-upgrade`.
- Users of Fedora (notably Fedora 24) has reported a `gcc` error saying the directory
`/usr/lib/rpm/redhat/redhat-hardened-cc1` is missing, despite `gcc` itself being installed. [The
- confirmed work-around](https://gist.github.com/yograterol/99c8e123afecc828cb8c) seems to be to
- install the `redhat-rpm-config` package with e.g. `sudo dnf install redhat-rpm-config`.
+ confirmed work-around](https://gist.github.com/yograterol/99c8e123afecc828cb8c) seems to be to install the `redhat-rpm-config` package with e.g. `sudo dnf install redhat-rpm-config`.
- Some users trying to set up a virtualenv on an NTFS filesystem find that it fails due to issues
- with symlinks not being supported. Answer is to not use NTFS (seriously, why would you do that to
- yourself?)
+ with symlinks not being supported. Answer is to not use NTFS (seriously, why would you do that to yourself?)
## Mac Troubleshooting
-- Mac users have reported a critical `MemoryError` when trying to start Evennia on Mac with a Python
- version below `2.7.12`. If you get this error, update to the latest XCode and Python2 version.
-- Some Mac users have reported not being able to connect to `localhost` (i.e. your own computer). If
- so, try to connect to `127.0.0.1` instead, which is the same thing. Use port 4000 from mud clients
- and port 4001 from the web browser as usual.
+- Some Mac users have reported not being able to connect to `localhost` (i.e. your own computer). If so, try to connect to `127.0.0.1` instead, which is the same thing. Use port 4000 from mud clients and port 4001 from the web browser as usual.
## Windows Troubleshooting
-- Install Python [from the Python homepage](https://www.python.org/downloads/windows/). You will
- need to be a Windows Administrator to install packages.
-- When installing Python, make sure to check-mark *all* install options, especially the one about making Python
- available on the path (you may have to scroll to see it). This allows you to
- just write `python` in any console without first finding where the `python`
- program actually sits on your hard drive.
-- If you get a `command not found` when trying to run the `evennia` program after installation, try closing the
- Console and starting it again (remember to re-activate the virtualenv!). Sometimes Windows is not updating
- its environment properly.
+- Install Python [from the Python homepage](https://www.python.org/downloads/windows/). You will need to be a Windows Administrator to install packages.
+- When installing Python, make sure to check-mark *all* install options, especially the one about making Python available on the path (you may have to scroll to see it). This allows you to
+ just write `python` in any console without first finding where the `python` program actually sits on your hard drive.
+- If you get a `command not found` when trying to run the `evennia` program after installation, try closing the Console and starting it again (remember to re-activate the virtualenv if you use one!). Sometimes Windows is not updating its environment properly and `evennia` will be available only in the new console.
- If you installed Python but the `python` command is not available (even in a new console), then
- you might have missed installing Python on the path. In the Windows Python installer you get a list
- of options for what to install. Most or all options are pre-checked except this one, and you may
- even have to scroll down to see it. Reinstall Python and make sure it's checked.
+ you might have missed installing Python on the path. In the Windows Python installer you get a list of options for what to install. Most or all options are pre-checked except this one, and you may even have to scroll down to see it. Reinstall Python and make sure it's checked.
- If your MUD client cannot connect to `localhost:4000`, try the equivalent `127.0.0.1:4000`
instead. Some MUD clients on Windows does not appear to understand the alias `localhost`.
- Some Windows users get an error installing the Twisted 'wheel'. A wheel is a pre-compiled binary
- package for Python. A common reason for this error is that you are using a 32-bit version of Python,
- but Twisted has not yet uploaded the latest 32-bit wheel. Easiest way to fix this is to install a
- slightly older Twisted version. So if, say, version `22.1` failed, install `22.0` manually with `pip
- install twisted==22.0`. Alternatively you could check that you are using the 64-bit version of Python
- and uninstall any 32bit one. If so, you must then `deactivate` the virtualenv, delete the `evenv` folder
- and recreate it anew with your new Python.
+ package for Python. A common reason for this error is that you are using a 32-bit version of Python, but Twisted has not yet uploaded the latest 32-bit wheel. Easiest way to fix this is to install a slightly older Twisted version. So if, say, version `22.1` failed, install `22.0` manually with `pip install twisted==22.0`. Alternatively you could check that you are using the 64-bit version of Python and uninstall any 32bit one. If so, you must then `deactivate` the virtualenv, delete the `evenv` folder and recreate it anew with your new Python.
- If your server won't start, with no error messages (and no log files at all when starting from
- scratch), try to start with `evennia ipstart` instead. If you then see an error about `system cannot
- find the path specified`, it may be that the file `evennia\evennia\server\twistd.bat` has the wrong
- path to the `twistd` executable. This file is auto-generated, so try to delete it and then run
- `evennia start` to rebuild it and see if it works. If it still doesn't work you need to open it in a
- text editor like Notepad. It's just one line containing the path to the `twistd.exe` executable as
- determined by Evennia. If you installed Twisted in a non-standard location this might be wrong and
- you should update the line to the real location.
+ scratch), try to start with `evennia ipstart` instead. If you then see an error about `system cannot find the path specified`, it may be that the file `evennia\evennia\server\twistd.bat` has the wrong path to the `twistd` executable. This file is auto-generated, so try to delete it and then run `evennia start` to rebuild it and see if it works. If it still doesn't work you need to open it in a text editor like Notepad. It's just one line containing the path to the `twistd.exe` executable as determined by Evennia. If you installed Twisted in a non-standard location this might be wrong and you should update the line to the real location.
- Some users have reported issues with Windows WSL and anti-virus software during Evennia
- development. Timeout errors and the inability to run `evennia connections` may be due to your anti-
- virus software interfering. Try disabling or changing your anti-virus software settings.
+ development. Timeout errors and the inability to run `evennia connections` may be due to your anti-virus software interfering. Try disabling or changing your anti-virus software settings.
diff --git a/docs/1.0-dev/_sources/Setup/Installation-Upgrade.md.txt b/docs/1.0-dev/_sources/Setup/Installation-Upgrade.md.txt
index 50f952f0ad..8b784e5f40 100644
--- a/docs/1.0-dev/_sources/Setup/Installation-Upgrade.md.txt
+++ b/docs/1.0-dev/_sources/Setup/Installation-Upgrade.md.txt
@@ -1,45 +1,44 @@
# Upgrading an existing installation
+This is relevant to you already having code in an older Evennia version. If you are new, or don't have much code yet, it may be easier to just start fresh with the [Installation](./Installation.md) instructions and copy
+over things manually.
+
## Evennia v0.9.5 to 1.0
+### Upgrading the Evennia library
+
Prior to 1.0, all Evennia installs were [Git-installs](./Installation-Git.md). These instructions
assume that you have a cloned `evennia` repo and use a virtualenv (best practices).
- Make sure to stop Evennia 0.9.5 entirely with `evennia stop`.
- `deactivate` to leave your active virtualenv.
- Make a _backup_ of your entire `mygame` folder, just to be sure!
-- Delete the old `evenv` folder, or rename it (in case you want to keep using 0.9.5 for a while).
-- Install Python 3.10 (recommended) or 3.9. Follow the [Git-installation](./Installation-Git.md) for your OS if needed.
-- If using virtualenv, make a _new_ one with `python3.10 -m venv evenv`, then activate with `source evenv/bin/activate`
- (linux/mac) or `\evenv\Script\activate` (windows)
-- `cd` into your `evennia/` folder (you want to see the `docs/`, `bin/` directories as well as a nested `evennia/` folder)
-- **Prior to 1.0 release only** - do `git checkout develop` to switch to the develop branch. After release, this will
- be found on the default master branch.
+- Install Python 3.11 (recommended). Follow the [Git-installation](./Installation-Git.md) for your OS if needed.
+- Delete the old virtualenv `evenv` folder, or rename it (in case you want to keep using 0.9.5 for a while).
+- Make _new_ `evenv` virtualenv (see the [virtualenv instructions](./Installation-Git.md#virtualenv) for help)
+- `cd` into your `evennia/` root folder (you want to see the `docs/` and `bin/` directories as well as a nested `evennia/` folder)
- `git pull`
- `pip install -e .`
-- If you want the optional extra libs, do `pip install -r requirements_extra.txt`.
+- If you want the optional extra libs (needed by some contribs), do `pip install -e .[extra]`
- Test that you can run the `evennia` command.
+### Upgrading your game dir
+
If you don't have anything you want to keep in your existing game dir, you can just start a new onew
using the normal [install instructions](./Installation.md). If you want to keep/convert your existing
game dir, continue below.
- First, make a backup of your exising game dir! If you use version control, make sure to commit your current state.
- `cd` to your existing 0.9.5-based game folder (like `mygame`.)
-- If you have changed `mygame/web`, _rename_ the folder to `web_0.9.5`. If you didn't change anything (or don't have
-anything you want to keep), you can _delete_ it entirely.
-- Copy `evennia/evennia/game_template/web` to `mygame/` (e.g. using `cp -Rf` or a file manager). This new `web` folder
-replaces the old one and has a very different structure.
+- If you have changed `mygame/web`, _rename_ the folder to `web_0.9.5`. If you didn't change anything (or don't have anything you want to keep), you can _delete_ it entirely.
+- Copy `evennia/evennia/game_template/web` to `mygame/` (e.g. using `cp -Rf` or a file manager). This new `web` folder _replaces the old one_ and has a very different structure.
- It's possible you need to replace/comment out import and calls to the deprecated
-[`django.conf.urls`](https://docs.djangoproject.com/en/3.2/ref/urls/#url). The new way to call it is
-[available here](https://docs.djangoproject.com/en/4.0/ref/urls/#django.urls.re_path).
+[`django.conf.urls`](https://docs.djangoproject.com/en/3.2/ref/urls/#url). The new way to call it is [available here](https://docs.djangoproject.com/en/4.0/ref/urls/#django.urls.re_path).
- Run `evennia migrate`
- Run `evennia start`
-If you made extensive work in your game dir, you may well find that you need to do some (hopefully minor)
-changes to your code before it will start with Evennia 1.0. Some important points:
+If you made extensive work in your game dir, you may well find that you need to do some (hopefully minor) changes to your code before it will start with Evennia 1.0. Some important points:
-- The `evennia/contrib/` folder changed structure - there are now categorized sub-folders, so you have to update
-your imports.
+- The `evennia/contrib/` folder changed structure - there are now categorized sub-folders, so you have to update your imports.
- Any `web` changes need to be moved back from your backup into the new structure of `web/` manually.
- See the [Evennia 1.0 Changelog](../Coding/Changelog.md) for all changes.
diff --git a/docs/1.0-dev/_sources/Setup/Installation.md.txt b/docs/1.0-dev/_sources/Setup/Installation.md.txt
index 6a6fa7232c..42d96df8f8 100644
--- a/docs/1.0-dev/_sources/Setup/Installation.md.txt
+++ b/docs/1.0-dev/_sources/Setup/Installation.md.txt
@@ -1,31 +1,33 @@
# Installation
-```{warning}
-pip install evennia is not yet available in develop branch. Use the [git installation](./Installation-Git.md).
-```
```{important}
-If you are converting an existing game from a previous version, [see here](./Installation-Upgrade.md).
+If you are converting an existing game from a previous Evennia version, [see here](./Installation-Upgrade.md).
```
+Installing Evennia doesn't make anything visible online. Apart from installation and updating, you can develop your game without any internet connection.
-- Evennia requires [Python](https://www.python.org/downloads/) 3.9 or 3.10.
-- Using a [Python virtualenv](../Glossary.md#virtualenv) is highly recommended in order to keep your
+- Evennia supports [Python](https://www.python.org/downloads/) 3.9, 3.10 or 3.11.
+- Using a [Python virtualenv](./Installation-Git.md#virtualenv) is optional, but _highly recommended_ in order to keep your
Evennia installation independent from the system libraries.
-- Don't install Evennia as
-administrator or superuser.
+- Don't install Evennia as administrator or superuser.
+- If you run into trouble, see [installation troubleshooting](./Installation-Troubleshooting.md).
-Evennia is managed from the terminal (console/CMD on Windows). If you have a suitable Python installed, you can install with
+Evennia is managed from the terminal (console/CMD on Windows). If you have a suitable Python installed, you can install it with
pip install evennia
-Alternatively, you can [install Evennia from github](./Installation-Git.md) or use [docker](./Installation-Docker.md).
+Optional: If you use a [contrib](Contribs) that warns you that it needs additional packages, you can
+install all extra dependencies with
-Installing doesn't make anything visible online. Apart from installation and updating, you can develop your game without any internet connection.
+ pip install evennia[extra]
+
+To update Evennia later, do
+
+ pip install --upgrade evennia
Once installed, make sure the `evennia` command works. Use `evennia -h` for usage help. If you are using a virtualenv, make sure it's active whenever you need to use the `evennia` command.
-> Check out [installation troubleshooting](./Installation-Troubleshooting.md) if you run into problems. Some
-users have also experimented with [installing Evennia on Android](./Installation-Android.md).
+> You can also [clone Evennia from github](./Installation-Git.md) or use [docker](./Installation-Docker.md). Some users have also experimented with [installing Evennia on Android](./Installation-Android.md).
## Initialize a new game
@@ -82,13 +84,12 @@ or
evennia -l
+Stop viewing the log by pressing `Ctrl-C` (`Cmd-C` for Mac).
-You can start viewing the log immediately when running `evennia` commands, such as
-
+You can start viewing the log immediately by adding `-l/--log` to `evennia` commands, such as
evennia start -l
-To exit the log tailing, enter `Ctrl-C` (`Cmd-C` for Mac). This will not affect the server.
## Server configuration
@@ -96,7 +97,7 @@ The server configuration file is `mygame/server/settings.py`. It's empty by defa
## Register with the Evennia Game Index (optional)
-You can optionally let the world know that you are working on a new Evennia-based game by
+You can let the world know that you are working on a new Evennia-based game by
registering your server with the _Evennia game index_. You don't have to be
open for players to do this - you just mark your game as closed and "pre-alpha".
diff --git a/docs/1.0-dev/_sources/index.md.txt b/docs/1.0-dev/_sources/index.md.txt
index e8950126c8..7c81d0ea98 100644
--- a/docs/1.0-dev/_sources/index.md.txt
+++ b/docs/1.0-dev/_sources/index.md.txt
@@ -16,9 +16,9 @@ This is the manual of [Evennia](https://www.evennia.com), the open source Python
- [Evennia Introduction](./Evennia-Introduction.md)
- [How to contribute and get help](./Contributing.md)
-## Setup
+## Setup
-- [Installation](Setup/Installation.md)
+- [Installation](Setup/Installation.md)
- [Starting, Stopping and Reloading](Setup/Start-Stop-Reload.md) - how to manage the Evennia server
- [Configurations](Setup/Setup-Overview.md) - how to run, maintain and release
@@ -35,7 +35,7 @@ This is the manual of [Evennia](https://www.evennia.com), the open source Python
- [Default Commands](Components/Default-Commands.md) - list of game commands included out of the box
- [Coding](Coding/Coding-Overview.md) - coding and development hints and resources
-## Contributions and Info
+## Contributions and Info
- [Contribs](Contribs/Contribs-Overview.md) - game-specific code and snippets to use for your game
- [How to contribute to these docs](./Contributing-Docs.md) - if you want to help out
@@ -46,7 +46,7 @@ This is the manual of [Evennia](https://www.evennia.com), the open source Python
## All Sections
-
+
Click here to expand the full list of Documentation sections.
diff --git a/docs/1.0-dev/api/evennia.commands.default.account.html b/docs/1.0-dev/api/evennia.commands.default.account.html
index b8c1c7e5ff..e271df0c7e 100644
--- a/docs/1.0-dev/api/evennia.commands.default.account.html
+++ b/docs/1.0-dev/api/evennia.commands.default.account.html
@@ -133,7 +133,7 @@ method. Otherwise all text will be returned to all connected sessions.
-search_index_entry = {'aliases': '@update @swap @parent @type @typeclasses', 'category': 'building', 'key': '@typeclass', 'no_prefix': 'typeclass update swap parent type typeclasses', 'tags': '', 'text': "\n set or change an object's typeclass\n\n Usage:\n typeclass[/switch] <object> [= typeclass.path]\n typeclass/prototype <object> = prototype_key\n\n typeclasses or typeclass/list/show [typeclass.path]\n swap - this is a shorthand for using /force/reset flags.\n update - this is a shorthand for using the /force/reload flag.\n\n Switch:\n show, examine - display the current typeclass of object (default) or, if\n given a typeclass path, show the docstring of that typeclass.\n update - *only* re-run at_object_creation on this object\n meaning locks or other properties set later may remain.\n reset - clean out *all* the attributes and properties on the\n object - basically making this a new clean object. This will also\n reset cmdsets!\n force - change to the typeclass also if the object\n already has a typeclass of the same name.\n list - show available typeclasses. Only typeclasses in modules actually\n imported or used from somewhere in the code will show up here\n (those typeclasses are still available if you know the path)\n prototype - clean and overwrite the object with the specified\n prototype key - effectively making a whole new object.\n\n Example:\n type button = examples.red_button.RedButton\n type/prototype button=a red button\n\n If the typeclass_path is not given, the current object's typeclass is\n assumed.\n\n View or set an object's typeclass. If setting, the creation hooks of the\n new typeclass will be run on the object. If you have clashing properties on\n the old class, use /reset. By default you are protected from changing to a\n typeclass of the same name as the one you already have - use /force to\n override this protection.\n\n The given typeclass must be identified by its location using python\n dot-notation pointing to the correct module and class. If no typeclass is\n given (or a wrong typeclass is given). Errors in the path or new typeclass\n will lead to the old typeclass being kept. The location of the typeclass\n module is searched from the default typeclass directory, as defined in the\n server settings.\n\n "}¶
+search_index_entry = {'aliases': '@typeclasses @type @swap @parent @update', 'category': 'building', 'key': '@typeclass', 'no_prefix': 'typeclass typeclasses type swap parent update', 'tags': '', 'text': "\n set or change an object's typeclass\n\n Usage:\n typeclass[/switch] <object> [= typeclass.path]\n typeclass/prototype <object> = prototype_key\n\n typeclasses or typeclass/list/show [typeclass.path]\n swap - this is a shorthand for using /force/reset flags.\n update - this is a shorthand for using the /force/reload flag.\n\n Switch:\n show, examine - display the current typeclass of object (default) or, if\n given a typeclass path, show the docstring of that typeclass.\n update - *only* re-run at_object_creation on this object\n meaning locks or other properties set later may remain.\n reset - clean out *all* the attributes and properties on the\n object - basically making this a new clean object. This will also\n reset cmdsets!\n force - change to the typeclass also if the object\n already has a typeclass of the same name.\n list - show available typeclasses. Only typeclasses in modules actually\n imported or used from somewhere in the code will show up here\n (those typeclasses are still available if you know the path)\n prototype - clean and overwrite the object with the specified\n prototype key - effectively making a whole new object.\n\n Example:\n type button = examples.red_button.RedButton\n type/prototype button=a red button\n\n If the typeclass_path is not given, the current object's typeclass is\n assumed.\n\n View or set an object's typeclass. If setting, the creation hooks of the\n new typeclass will be run on the object. If you have clashing properties on\n the old class, use /reset. By default you are protected from changing to a\n typeclass of the same name as the one you already have - use /force to\n override this protection.\n\n The given typeclass must be identified by its location using python\n dot-notation pointing to the correct module and class. If no typeclass is\n given (or a wrong typeclass is given). Errors in the path or new typeclass\n will lead to the old typeclass being kept. The location of the typeclass\n module is searched from the default typeclass directory, as defined in the\n server settings.\n\n "}¶
-search_index_entry = {'aliases': '@locate @search', 'category': 'building', 'key': '@find', 'no_prefix': 'find locate search', 'tags': '', 'text': '\n search the database for objects\n\n Usage:\n find[/switches] <name or dbref or *account> [= dbrefmin[-dbrefmax]]\n locate - this is a shorthand for using the /loc switch.\n\n Switches:\n room - only look for rooms (location=None)\n exit - only look for exits (destination!=None)\n char - only look for characters (BASE_CHARACTER_TYPECLASS)\n exact - only exact matches are returned.\n loc - display object location if exists and match has one result\n startswith - search for names starting with the string, rather than containing\n\n Searches the database for an object of a particular name or exact #dbref.\n Use *accountname to search for an account. The switches allows for\n limiting object matches to certain game entities. Dbrefmin and dbrefmax\n limits matches to within the given dbrefs range, or above/below if only\n one is given.\n '}¶
+search_index_entry = {'aliases': '@search @locate', 'category': 'building', 'key': '@find', 'no_prefix': 'find search locate', 'tags': '', 'text': '\n search the database for objects\n\n Usage:\n find[/switches] <name or dbref or *account> [= dbrefmin[-dbrefmax]]\n locate - this is a shorthand for using the /loc switch.\n\n Switches:\n room - only look for rooms (location=None)\n exit - only look for exits (destination!=None)\n char - only look for characters (BASE_CHARACTER_TYPECLASS)\n exact - only exact matches are returned.\n loc - display object location if exists and match has one result\n startswith - search for names starting with the string, rather than containing\n\n Searches the database for an object of a particular name or exact #dbref.\n Use *accountname to search for an account. The switches allows for\n limiting object matches to certain game entities. Dbrefmin and dbrefmax\n limits matches to within the given dbrefs range, or above/below if only\n one is given.\n '}¶
diff --git a/docs/1.0-dev/api/evennia.commands.default.comms.html b/docs/1.0-dev/api/evennia.commands.default.comms.html
index c2f746c095..c78ef6328f 100644
--- a/docs/1.0-dev/api/evennia.commands.default.comms.html
+++ b/docs/1.0-dev/api/evennia.commands.default.comms.html
@@ -256,7 +256,7 @@ ban mychannel1,mychannel2= EvilUser : Was banned for spamming.
-search_index_entry = {'aliases': '@channels @chan', 'category': 'comms', 'key': '@channel', 'no_prefix': 'channel channels chan', 'tags': '', 'text': "\n Use and manage in-game channels.\n\n Usage:\n channel channelname <msg>\n channel channel name = <msg>\n channel (show all subscription)\n channel/all (show available channels)\n channel/alias channelname = alias[;alias...]\n channel/unalias alias\n channel/who channelname\n channel/history channelname [= index]\n channel/sub channelname [= alias[;alias...]]\n channel/unsub channelname[,channelname, ...]\n channel/mute channelname[,channelname,...]\n channel/unmute channelname[,channelname,...]\n\n channel/create channelname[;alias;alias[:typeclass]] [= description]\n channel/destroy channelname [= reason]\n channel/desc channelname = description\n channel/lock channelname = lockstring\n channel/unlock channelname = lockstring\n channel/ban channelname (list bans)\n channel/ban[/quiet] channelname[, channelname, ...] = subscribername [: reason]\n channel/unban[/quiet] channelname[, channelname, ...] = subscribername\n channel/boot[/quiet] channelname[,channelname,...] = subscribername [: reason]\n\n # subtopics\n\n ## sending\n\n Usage: channel channelname msg\n channel channel name = msg (with space in channel name)\n\n This sends a message to the channel. Note that you will rarely use this\n command like this; instead you can use the alias\n\n channelname <msg>\n channelalias <msg>\n\n For example\n\n public Hello World\n pub Hello World\n\n (this shortcut doesn't work for aliases containing spaces)\n\n See channel/alias for help on setting channel aliases.\n\n ## alias and unalias\n\n Usage: channel/alias channel = alias[;alias[;alias...]]\n channel/unalias alias\n channel - this will list your subs and aliases to each channel\n\n Set one or more personal aliases for referencing a channel. For example:\n\n channel/alias warrior's guild = warrior;wguild;warchannel;warrior guild\n\n You can now send to the channel using all of these:\n\n warrior's guild Hello\n warrior Hello\n wguild Hello\n warchannel Hello\n\n Note that this will not work if the alias has a space in it. So the\n 'warrior guild' alias must be used with the `channel` command:\n\n channel warrior guild = Hello\n\n Channel-aliases can be removed one at a time, using the '/unalias' switch.\n\n ## who\n\n Usage: channel/who channelname\n\n List the channel's subscribers. Shows who are currently offline or are\n muting the channel. Subscribers who are 'muting' will not see messages sent\n to the channel (use channel/mute to mute a channel).\n\n ## history\n\n Usage: channel/history channel [= index]\n\n This will display the last |c20|n lines of channel history. By supplying an\n index number, you will step that many lines back before viewing those 20 lines.\n\n For example:\n\n channel/history public = 35\n\n will go back 35 lines and show the previous 20 lines from that point (so\n lines -35 to -55).\n\n ## sub and unsub\n\n Usage: channel/sub channel [=alias[;alias;...]]\n channel/unsub channel\n\n This subscribes you to a channel and optionally assigns personal shortcuts\n for you to use to send to that channel (see aliases). When you unsub, all\n your personal aliases will also be removed.\n\n ## mute and unmute\n\n Usage: channel/mute channelname\n channel/unmute channelname\n\n Muting silences all output from the channel without actually\n un-subscribing. Other channel members will see that you are muted in the /who\n list. Sending a message to the channel will automatically unmute you.\n\n ## create and destroy\n\n Usage: channel/create channelname[;alias;alias[:typeclass]] [= description]\n channel/destroy channelname [= reason]\n\n Creates a new channel (or destroys one you control). You will automatically\n join the channel you create and everyone will be kicked and loose all aliases\n to a destroyed channel.\n\n ## lock and unlock\n\n Usage: channel/lock channelname = lockstring\n channel/unlock channelname = lockstring\n\n Note: this is an admin command.\n\n A lockstring is on the form locktype:lockfunc(). Channels understand three\n locktypes:\n listen - who may listen or join the channel.\n send - who may send messages to the channel\n control - who controls the channel. This is usually the one creating\n the channel.\n\n Common lockfuncs are all() and perm(). To make a channel everyone can\n listen to but only builders can talk on, use this:\n\n listen:all()\n send: perm(Builders)\n\n ## boot and ban\n\n Usage:\n channel/boot[/quiet] channelname[,channelname,...] = subscribername [: reason]\n channel/ban channelname[, channelname, ...] = subscribername [: reason]\n channel/unban channelname[, channelname, ...] = subscribername\n channel/unban channelname\n channel/ban channelname (list bans)\n\n Booting will kick a named subscriber from channel(s) temporarily. The\n 'reason' will be passed to the booted user. Unless the /quiet switch is\n used, the channel will also be informed of the action. A booted user is\n still able to re-connect, but they'll have to set up their aliases again.\n\n Banning will blacklist a user from (re)joining the provided channels. It\n will then proceed to boot them from those channels if they were connected.\n The 'reason' and `/quiet` works the same as for booting.\n\n Example:\n boot mychannel1 = EvilUser : Kicking you to cool down a bit.\n ban mychannel1,mychannel2= EvilUser : Was banned for spamming.\n\n "}¶
+search_index_entry = {'aliases': '@chan @channels', 'category': 'comms', 'key': '@channel', 'no_prefix': 'channel chan channels', 'tags': '', 'text': "\n Use and manage in-game channels.\n\n Usage:\n channel channelname <msg>\n channel channel name = <msg>\n channel (show all subscription)\n channel/all (show available channels)\n channel/alias channelname = alias[;alias...]\n channel/unalias alias\n channel/who channelname\n channel/history channelname [= index]\n channel/sub channelname [= alias[;alias...]]\n channel/unsub channelname[,channelname, ...]\n channel/mute channelname[,channelname,...]\n channel/unmute channelname[,channelname,...]\n\n channel/create channelname[;alias;alias[:typeclass]] [= description]\n channel/destroy channelname [= reason]\n channel/desc channelname = description\n channel/lock channelname = lockstring\n channel/unlock channelname = lockstring\n channel/ban channelname (list bans)\n channel/ban[/quiet] channelname[, channelname, ...] = subscribername [: reason]\n channel/unban[/quiet] channelname[, channelname, ...] = subscribername\n channel/boot[/quiet] channelname[,channelname,...] = subscribername [: reason]\n\n # subtopics\n\n ## sending\n\n Usage: channel channelname msg\n channel channel name = msg (with space in channel name)\n\n This sends a message to the channel. Note that you will rarely use this\n command like this; instead you can use the alias\n\n channelname <msg>\n channelalias <msg>\n\n For example\n\n public Hello World\n pub Hello World\n\n (this shortcut doesn't work for aliases containing spaces)\n\n See channel/alias for help on setting channel aliases.\n\n ## alias and unalias\n\n Usage: channel/alias channel = alias[;alias[;alias...]]\n channel/unalias alias\n channel - this will list your subs and aliases to each channel\n\n Set one or more personal aliases for referencing a channel. For example:\n\n channel/alias warrior's guild = warrior;wguild;warchannel;warrior guild\n\n You can now send to the channel using all of these:\n\n warrior's guild Hello\n warrior Hello\n wguild Hello\n warchannel Hello\n\n Note that this will not work if the alias has a space in it. So the\n 'warrior guild' alias must be used with the `channel` command:\n\n channel warrior guild = Hello\n\n Channel-aliases can be removed one at a time, using the '/unalias' switch.\n\n ## who\n\n Usage: channel/who channelname\n\n List the channel's subscribers. Shows who are currently offline or are\n muting the channel. Subscribers who are 'muting' will not see messages sent\n to the channel (use channel/mute to mute a channel).\n\n ## history\n\n Usage: channel/history channel [= index]\n\n This will display the last |c20|n lines of channel history. By supplying an\n index number, you will step that many lines back before viewing those 20 lines.\n\n For example:\n\n channel/history public = 35\n\n will go back 35 lines and show the previous 20 lines from that point (so\n lines -35 to -55).\n\n ## sub and unsub\n\n Usage: channel/sub channel [=alias[;alias;...]]\n channel/unsub channel\n\n This subscribes you to a channel and optionally assigns personal shortcuts\n for you to use to send to that channel (see aliases). When you unsub, all\n your personal aliases will also be removed.\n\n ## mute and unmute\n\n Usage: channel/mute channelname\n channel/unmute channelname\n\n Muting silences all output from the channel without actually\n un-subscribing. Other channel members will see that you are muted in the /who\n list. Sending a message to the channel will automatically unmute you.\n\n ## create and destroy\n\n Usage: channel/create channelname[;alias;alias[:typeclass]] [= description]\n channel/destroy channelname [= reason]\n\n Creates a new channel (or destroys one you control). You will automatically\n join the channel you create and everyone will be kicked and loose all aliases\n to a destroyed channel.\n\n ## lock and unlock\n\n Usage: channel/lock channelname = lockstring\n channel/unlock channelname = lockstring\n\n Note: this is an admin command.\n\n A lockstring is on the form locktype:lockfunc(). Channels understand three\n locktypes:\n listen - who may listen or join the channel.\n send - who may send messages to the channel\n control - who controls the channel. This is usually the one creating\n the channel.\n\n Common lockfuncs are all() and perm(). To make a channel everyone can\n listen to but only builders can talk on, use this:\n\n listen:all()\n send: perm(Builders)\n\n ## boot and ban\n\n Usage:\n channel/boot[/quiet] channelname[,channelname,...] = subscribername [: reason]\n channel/ban channelname[, channelname, ...] = subscribername [: reason]\n channel/unban channelname[, channelname, ...] = subscribername\n channel/unban channelname\n channel/ban channelname (list bans)\n\n Booting will kick a named subscriber from channel(s) temporarily. The\n 'reason' will be passed to the booted user. Unless the /quiet switch is\n used, the channel will also be informed of the action. A booted user is\n still able to re-connect, but they'll have to set up their aliases again.\n\n Banning will blacklist a user from (re)joining the provided channels. It\n will then proceed to boot them from those channels if they were connected.\n The 'reason' and `/quiet` works the same as for booting.\n\n Example:\n boot mychannel1 = EvilUser : Kicking you to cool down a bit.\n ban mychannel1,mychannel2= EvilUser : Was banned for spamming.\n\n "}¶
@@ -935,7 +935,7 @@ ban mychannel1,mychannel2= EvilUser : Was banned for spamming.
@@ -955,7 +955,7 @@ ban mychannel1,mychannel2= EvilUser : Was banned for spamming.
-search_index_entry = {'aliases': '@channels @chan', 'category': 'comms', 'key': '@channel', 'no_prefix': 'channel channels chan', 'tags': '', 'text': "\n Use and manage in-game channels.\n\n Usage:\n channel channelname <msg>\n channel channel name = <msg>\n channel (show all subscription)\n channel/all (show available channels)\n channel/alias channelname = alias[;alias...]\n channel/unalias alias\n channel/who channelname\n channel/history channelname [= index]\n channel/sub channelname [= alias[;alias...]]\n channel/unsub channelname[,channelname, ...]\n channel/mute channelname[,channelname,...]\n channel/unmute channelname[,channelname,...]\n\n channel/create channelname[;alias;alias[:typeclass]] [= description]\n channel/destroy channelname [= reason]\n channel/desc channelname = description\n channel/lock channelname = lockstring\n channel/unlock channelname = lockstring\n channel/ban channelname (list bans)\n channel/ban[/quiet] channelname[, channelname, ...] = subscribername [: reason]\n channel/unban[/quiet] channelname[, channelname, ...] = subscribername\n channel/boot[/quiet] channelname[,channelname,...] = subscribername [: reason]\n\n # subtopics\n\n ## sending\n\n Usage: channel channelname msg\n channel channel name = msg (with space in channel name)\n\n This sends a message to the channel. Note that you will rarely use this\n command like this; instead you can use the alias\n\n channelname <msg>\n channelalias <msg>\n\n For example\n\n public Hello World\n pub Hello World\n\n (this shortcut doesn't work for aliases containing spaces)\n\n See channel/alias for help on setting channel aliases.\n\n ## alias and unalias\n\n Usage: channel/alias channel = alias[;alias[;alias...]]\n channel/unalias alias\n channel - this will list your subs and aliases to each channel\n\n Set one or more personal aliases for referencing a channel. For example:\n\n channel/alias warrior's guild = warrior;wguild;warchannel;warrior guild\n\n You can now send to the channel using all of these:\n\n warrior's guild Hello\n warrior Hello\n wguild Hello\n warchannel Hello\n\n Note that this will not work if the alias has a space in it. So the\n 'warrior guild' alias must be used with the `channel` command:\n\n channel warrior guild = Hello\n\n Channel-aliases can be removed one at a time, using the '/unalias' switch.\n\n ## who\n\n Usage: channel/who channelname\n\n List the channel's subscribers. Shows who are currently offline or are\n muting the channel. Subscribers who are 'muting' will not see messages sent\n to the channel (use channel/mute to mute a channel).\n\n ## history\n\n Usage: channel/history channel [= index]\n\n This will display the last |c20|n lines of channel history. By supplying an\n index number, you will step that many lines back before viewing those 20 lines.\n\n For example:\n\n channel/history public = 35\n\n will go back 35 lines and show the previous 20 lines from that point (so\n lines -35 to -55).\n\n ## sub and unsub\n\n Usage: channel/sub channel [=alias[;alias;...]]\n channel/unsub channel\n\n This subscribes you to a channel and optionally assigns personal shortcuts\n for you to use to send to that channel (see aliases). When you unsub, all\n your personal aliases will also be removed.\n\n ## mute and unmute\n\n Usage: channel/mute channelname\n channel/unmute channelname\n\n Muting silences all output from the channel without actually\n un-subscribing. Other channel members will see that you are muted in the /who\n list. Sending a message to the channel will automatically unmute you.\n\n ## create and destroy\n\n Usage: channel/create channelname[;alias;alias[:typeclass]] [= description]\n channel/destroy channelname [= reason]\n\n Creates a new channel (or destroys one you control). You will automatically\n join the channel you create and everyone will be kicked and loose all aliases\n to a destroyed channel.\n\n ## lock and unlock\n\n Usage: channel/lock channelname = lockstring\n channel/unlock channelname = lockstring\n\n Note: this is an admin command.\n\n A lockstring is on the form locktype:lockfunc(). Channels understand three\n locktypes:\n listen - who may listen or join the channel.\n send - who may send messages to the channel\n control - who controls the channel. This is usually the one creating\n the channel.\n\n Common lockfuncs are all() and perm(). To make a channel everyone can\n listen to but only builders can talk on, use this:\n\n listen:all()\n send: perm(Builders)\n\n ## boot and ban\n\n Usage:\n channel/boot[/quiet] channelname[,channelname,...] = subscribername [: reason]\n channel/ban channelname[, channelname, ...] = subscribername [: reason]\n channel/unban channelname[, channelname, ...] = subscribername\n channel/unban channelname\n channel/ban channelname (list bans)\n\n Booting will kick a named subscriber from channel(s) temporarily. The\n 'reason' will be passed to the booted user. Unless the /quiet switch is\n used, the channel will also be informed of the action. A booted user is\n still able to re-connect, but they'll have to set up their aliases again.\n\n Banning will blacklist a user from (re)joining the provided channels. It\n will then proceed to boot them from those channels if they were connected.\n The 'reason' and `/quiet` works the same as for booting.\n\n Example:\n boot mychannel1 = EvilUser : Kicking you to cool down a bit.\n ban mychannel1,mychannel2= EvilUser : Was banned for spamming.\n\n "}¶
+search_index_entry = {'aliases': '@chan @channels', 'category': 'comms', 'key': '@channel', 'no_prefix': 'channel chan channels', 'tags': '', 'text': "\n Use and manage in-game channels.\n\n Usage:\n channel channelname <msg>\n channel channel name = <msg>\n channel (show all subscription)\n channel/all (show available channels)\n channel/alias channelname = alias[;alias...]\n channel/unalias alias\n channel/who channelname\n channel/history channelname [= index]\n channel/sub channelname [= alias[;alias...]]\n channel/unsub channelname[,channelname, ...]\n channel/mute channelname[,channelname,...]\n channel/unmute channelname[,channelname,...]\n\n channel/create channelname[;alias;alias[:typeclass]] [= description]\n channel/destroy channelname [= reason]\n channel/desc channelname = description\n channel/lock channelname = lockstring\n channel/unlock channelname = lockstring\n channel/ban channelname (list bans)\n channel/ban[/quiet] channelname[, channelname, ...] = subscribername [: reason]\n channel/unban[/quiet] channelname[, channelname, ...] = subscribername\n channel/boot[/quiet] channelname[,channelname,...] = subscribername [: reason]\n\n # subtopics\n\n ## sending\n\n Usage: channel channelname msg\n channel channel name = msg (with space in channel name)\n\n This sends a message to the channel. Note that you will rarely use this\n command like this; instead you can use the alias\n\n channelname <msg>\n channelalias <msg>\n\n For example\n\n public Hello World\n pub Hello World\n\n (this shortcut doesn't work for aliases containing spaces)\n\n See channel/alias for help on setting channel aliases.\n\n ## alias and unalias\n\n Usage: channel/alias channel = alias[;alias[;alias...]]\n channel/unalias alias\n channel - this will list your subs and aliases to each channel\n\n Set one or more personal aliases for referencing a channel. For example:\n\n channel/alias warrior's guild = warrior;wguild;warchannel;warrior guild\n\n You can now send to the channel using all of these:\n\n warrior's guild Hello\n warrior Hello\n wguild Hello\n warchannel Hello\n\n Note that this will not work if the alias has a space in it. So the\n 'warrior guild' alias must be used with the `channel` command:\n\n channel warrior guild = Hello\n\n Channel-aliases can be removed one at a time, using the '/unalias' switch.\n\n ## who\n\n Usage: channel/who channelname\n\n List the channel's subscribers. Shows who are currently offline or are\n muting the channel. Subscribers who are 'muting' will not see messages sent\n to the channel (use channel/mute to mute a channel).\n\n ## history\n\n Usage: channel/history channel [= index]\n\n This will display the last |c20|n lines of channel history. By supplying an\n index number, you will step that many lines back before viewing those 20 lines.\n\n For example:\n\n channel/history public = 35\n\n will go back 35 lines and show the previous 20 lines from that point (so\n lines -35 to -55).\n\n ## sub and unsub\n\n Usage: channel/sub channel [=alias[;alias;...]]\n channel/unsub channel\n\n This subscribes you to a channel and optionally assigns personal shortcuts\n for you to use to send to that channel (see aliases). When you unsub, all\n your personal aliases will also be removed.\n\n ## mute and unmute\n\n Usage: channel/mute channelname\n channel/unmute channelname\n\n Muting silences all output from the channel without actually\n un-subscribing. Other channel members will see that you are muted in the /who\n list. Sending a message to the channel will automatically unmute you.\n\n ## create and destroy\n\n Usage: channel/create channelname[;alias;alias[:typeclass]] [= description]\n channel/destroy channelname [= reason]\n\n Creates a new channel (or destroys one you control). You will automatically\n join the channel you create and everyone will be kicked and loose all aliases\n to a destroyed channel.\n\n ## lock and unlock\n\n Usage: channel/lock channelname = lockstring\n channel/unlock channelname = lockstring\n\n Note: this is an admin command.\n\n A lockstring is on the form locktype:lockfunc(). Channels understand three\n locktypes:\n listen - who may listen or join the channel.\n send - who may send messages to the channel\n control - who controls the channel. This is usually the one creating\n the channel.\n\n Common lockfuncs are all() and perm(). To make a channel everyone can\n listen to but only builders can talk on, use this:\n\n listen:all()\n send: perm(Builders)\n\n ## boot and ban\n\n Usage:\n channel/boot[/quiet] channelname[,channelname,...] = subscribername [: reason]\n channel/ban channelname[, channelname, ...] = subscribername [: reason]\n channel/unban channelname[, channelname, ...] = subscribername\n channel/unban channelname\n channel/ban channelname (list bans)\n\n Booting will kick a named subscriber from channel(s) temporarily. The\n 'reason' will be passed to the booted user. Unless the /quiet switch is\n used, the channel will also be informed of the action. A booted user is\n still able to re-connect, but they'll have to set up their aliases again.\n\n Banning will blacklist a user from (re)joining the provided channels. It\n will then proceed to boot them from those channels if they were connected.\n The 'reason' and `/quiet` works the same as for booting.\n\n Example:\n boot mychannel1 = EvilUser : Kicking you to cool down a bit.\n ban mychannel1,mychannel2= EvilUser : Was banned for spamming.\n\n "}¶
-search_index_entry = {'aliases': 'l ls', 'category': 'general', 'key': 'look', 'no_prefix': ' l ls', 'tags': '', 'text': '\n look at location or object\n\n Usage:\n look\n look <obj>\n look *<account>\n\n Observes your location or objects in your vicinity.\n '}¶
+search_index_entry = {'aliases': 'ls l', 'category': 'general', 'key': 'look', 'no_prefix': ' ls l', 'tags': '', 'text': '\n look at location or object\n\n Usage:\n look\n look <obj>\n look *<account>\n\n Observes your location or objects in your vicinity.\n '}¶
@@ -268,7 +268,7 @@ for everyone to use, you need build privileges and the alias command.
@@ -300,7 +300,7 @@ for everyone to use, you need build privileges and the alias command.
-search_index_entry = {'aliases': 'nickname nicks', 'category': 'general', 'key': 'nick', 'no_prefix': ' nickname nicks', 'tags': '', 'text': '\n define a personal alias/nick by defining a string to\n match and replace it with another on the fly\n\n Usage:\n nick[/switches] <string> [= [replacement_string]]\n nick[/switches] <template> = <replacement_template>\n nick/delete <string> or number\n nicks\n\n Switches:\n inputline - replace on the inputline (default)\n object - replace on object-lookup\n account - replace on account-lookup\n list - show all defined aliases (also "nicks" works)\n delete - remove nick by index in /list\n clearall - clear all nicks\n\n Examples:\n nick hi = say Hello, I\'m Sarah!\n nick/object tom = the tall man\n nick build $1 $2 = create/drop $1;$2\n nick tell $1 $2=page $1=$2\n nick tm?$1=page tallman=$1\n nick tm\\=$1=page tallman=$1\n\n A \'nick\' is a personal string replacement. Use $1, $2, ... to catch arguments.\n Put the last $-marker without an ending space to catch all remaining text. You\n can also use unix-glob matching for the left-hand side <string>:\n\n * - matches everything\n ? - matches 0 or 1 single characters\n [abcd] - matches these chars in any order\n [!abcd] - matches everything not among these chars\n \\= - escape literal \'=\' you want in your <string>\n\n Note that no objects are actually renamed or changed by this command - your nicks\n are only available to you. If you want to permanently add keywords to an object\n for everyone to use, you need build privileges and the alias command.\n\n '}¶
+search_index_entry = {'aliases': 'nicks nickname', 'category': 'general', 'key': 'nick', 'no_prefix': ' nicks nickname', 'tags': '', 'text': '\n define a personal alias/nick by defining a string to\n match and replace it with another on the fly\n\n Usage:\n nick[/switches] <string> [= [replacement_string]]\n nick[/switches] <template> = <replacement_template>\n nick/delete <string> or number\n nicks\n\n Switches:\n inputline - replace on the inputline (default)\n object - replace on object-lookup\n account - replace on account-lookup\n list - show all defined aliases (also "nicks" works)\n delete - remove nick by index in /list\n clearall - clear all nicks\n\n Examples:\n nick hi = say Hello, I\'m Sarah!\n nick/object tom = the tall man\n nick build $1 $2 = create/drop $1;$2\n nick tell $1 $2=page $1=$2\n nick tm?$1=page tallman=$1\n nick tm\\=$1=page tallman=$1\n\n A \'nick\' is a personal string replacement. Use $1, $2, ... to catch arguments.\n Put the last $-marker without an ending space to catch all remaining text. You\n can also use unix-glob matching for the left-hand side <string>:\n\n * - matches everything\n ? - matches 0 or 1 single characters\n [abcd] - matches these chars in any order\n [!abcd] - matches everything not among these chars\n \\= - escape literal \'=\' you want in your <string>\n\n Note that no objects are actually renamed or changed by this command - your nicks\n are only available to you. If you want to permanently add keywords to an object\n for everyone to use, you need build privileges and the alias command.\n\n '}¶
@@ -598,7 +598,7 @@ placing it in their inventory.
@@ -629,7 +629,7 @@ placing it in their inventory.
-search_index_entry = {'aliases': '\' "', 'category': 'general', 'key': 'say', 'no_prefix': ' \' "', 'tags': '', 'text': '\n speak as your character\n\n Usage:\n say <message>\n\n Talk to those in your current location.\n '}¶
+search_index_entry = {'aliases': '" \'', 'category': 'general', 'key': 'say', 'no_prefix': ' " \'', 'tags': '', 'text': '\n speak as your character\n\n Usage:\n say <message>\n\n Talk to those in your current location.\n '}¶
@@ -709,7 +709,7 @@ automatically begin with your name.
-search_index_entry = {'aliases': 'emote :', 'category': 'general', 'key': 'pose', 'no_prefix': ' emote :', 'tags': '', 'text': "\n strike a pose\n\n Usage:\n pose <pose text>\n pose's <pose text>\n\n Example:\n pose is standing by the wall, smiling.\n -> others will see:\n Tom is standing by the wall, smiling.\n\n Describe an action being taken. The pose text will\n automatically begin with your name.\n "}¶
+search_index_entry = {'aliases': ': emote', 'category': 'general', 'key': 'pose', 'no_prefix': ' : emote', 'tags': '', 'text': "\n strike a pose\n\n Usage:\n pose <pose text>\n pose's <pose text>\n\n Example:\n pose is standing by the wall, smiling.\n -> others will see:\n Tom is standing by the wall, smiling.\n\n Describe an action being taken. The pose text will\n automatically begin with your name.\n "}¶
This is part of the Evennia unittest framework, for testing the
-stability and integrity of the codebase during updates. This module
-test the default command set. It is instantiated by the
-evennia/objects/tests.py module, which in turn is run by as part of the
-main test suite started with
(you may see this if a child command had no help text defined)
-
-
Usage:
command [args]
-
-
-
This is the base command class. Inherit from this
-to create new commands.
-
The cmdhandler makes the following variables available to the
-command methods (so you can always assume them to be there):
-
self.caller - the game object calling the command
-self.cmdstring - the command name used to trigger this command (allows
-
-
you to know which alias was used, for example)
-
-
-
self.args - everything supplied to the command following the cmdstring
(this is usually what is parsed in self.parse())
-
-
self.cmdset - the cmdset from which this command was matched (useful only
seldomly, notably for help-type commands, to create dynamic
-help entries and lists)
-
-
self.obj - the object on which this command is defined. If a default command,
this is usually the same as caller.
-
-
self.raw_string - the full raw string input, including the command name,
any args and no parsing.
-
-
-
The following class properties can/should be defined on your child class:
-
key - identifier for command (e.g. “look”)
-aliases - (optional) list of aliases (e.g. [“l”, “loo”])
-locks - lock string (default is “cmd:all()”)
-help_category - how to organize this help entry in help system
-
-
(default is “General”)
-
-
auto_help - defaults to True. Allows for turning off auto-help generation
-arg_regex - (optional) raw string regex defining how the argument part of
-
-
the command should look in order to match for this command
-(e.g. must it be a space between cmdname and arg?)
-
-
-
auto_help_display_key - (optional) if given, this replaces the string shown
in the auto-help listing. This is particularly useful for system-commands
-whose actual key is not really meaningful.
-
-
-
(Note that if auto_help is on, this initial string is also used by the
-system to create the help entry for the command, so it’s a good idea to
-format it similar to this one). This behavior can be changed by
-overriding the method ‘get_help’ of a command: by default, this
-method returns cmd.__doc__ (that is, this very docstring, or
-the docstring of your command). You can, however, extend or
-replace this without disabling auto_help.
Once the cmdhandler has identified this as the command we
-want, this function is run. If many of your commands have a
-similar syntax (for example ‘cmd arg1 = arg2’) you should
-simply define this once and just let other commands of the
-same form inherit from this. See the docstring of this module
-for which object properties are available to use (notably
-self.args).
This is the actual executing part of the command. It is
-called directly after self.parse(). See the docstring of this
-module for which object properties are available (beyond those
-set in self.parse())
-search_index_entry = {'aliases': '', 'category': 'general', 'key': 'interrupt', 'no_prefix': ' ', 'tags': '', 'text': '\n ## Base command\n\n (you may see this if a child command had no help text defined)\n\n Usage:\n command [args]\n\n This is the base command class. Inherit from this\n to create new commands.\n\n The cmdhandler makes the following variables available to the\n command methods (so you can always assume them to be there):\n\n self.caller - the game object calling the command\n self.cmdstring - the command name used to trigger this command (allows\n you to know which alias was used, for example)\n self.args - everything supplied to the command following the cmdstring\n (this is usually what is parsed in self.parse())\n self.cmdset - the cmdset from which this command was matched (useful only\n seldomly, notably for help-type commands, to create dynamic\n help entries and lists)\n self.obj - the object on which this command is defined. If a default command,\n this is usually the same as caller.\n self.raw_string - the full raw string input, including the command name,\n any args and no parsing.\n\n The following class properties can/should be defined on your child class:\n\n key - identifier for command (e.g. "look")\n aliases - (optional) list of aliases (e.g. ["l", "loo"])\n locks - lock string (default is "cmd:all()")\n help_category - how to organize this help entry in help system\n (default is "General")\n auto_help - defaults to True. Allows for turning off auto-help generation\n arg_regex - (optional) raw string regex defining how the argument part of\n the command should look in order to match for this command\n (e.g. must it be a space between cmdname and arg?)\n auto_help_display_key - (optional) if given, this replaces the string shown\n in the auto-help listing. This is particularly useful for system-commands\n whose actual key is not really meaningful.\n\n (Note that if auto_help is on, this initial string is also used by the\n system to create the help entry for the command, so it\'s a good idea to\n format it similar to this one). This behavior can be changed by\n overriding the method \'get_help\' of a command: by default, this\n method returns cmd.__doc__ (that is, this very docstring, or\n the docstring of your command). You can, however, extend or\n replace this without disabling auto_help.\n '}¶
@@ -157,7 +157,7 @@ there is no object yet before the account has logged in)
-search_index_entry = {'aliases': 'co conn con', 'category': 'general', 'key': 'connect', 'no_prefix': ' co conn con', 'tags': '', 'text': '\n connect to the game\n\n Usage (at login screen):\n connect accountname password\n connect "account name" "pass word"\n\n Use the create command to first create an account before logging in.\n\n If you have spaces in your name, enclose it in double quotes.\n '}¶
+search_index_entry = {'aliases': 'con co conn', 'category': 'general', 'key': 'connect', 'no_prefix': ' con co conn', 'tags': '', 'text': '\n connect to the game\n\n Usage (at login screen):\n connect accountname password\n connect "account name" "pass word"\n\n Use the create command to first create an account before logging in.\n\n If you have spaces in your name, enclose it in double quotes.\n '}¶
-search_index_entry = {'aliases': 'cre cr', 'category': 'general', 'key': 'create', 'no_prefix': ' cre cr', 'tags': '', 'text': '\n create a new account account\n\n Usage (at login screen):\n create <accountname> <password>\n create "account name" "pass word"\n\n This creates a new account account.\n\n If you have spaces in your name, enclose it in double quotes.\n '}¶
+search_index_entry = {'aliases': 'cr cre', 'category': 'general', 'key': 'create', 'no_prefix': ' cr cre', 'tags': '', 'text': '\n create a new account account\n\n Usage (at login screen):\n create <accountname> <password>\n create "account name" "pass word"\n\n This creates a new account account.\n\n If you have spaces in your name, enclose it in double quotes.\n '}¶
@@ -236,7 +236,7 @@ version is a bit more complicated.
@@ -262,7 +262,7 @@ version is a bit more complicated.
-search_index_entry = {'aliases': 'qu q', 'category': 'general', 'key': 'quit', 'no_prefix': ' qu q', 'tags': '', 'text': '\n quit when in unlogged-in state\n\n Usage:\n quit\n\n We maintain a different version of the quit command\n here for unconnected accounts for the sake of simplicity. The logged in\n version is a bit more complicated.\n '}¶
+search_index_entry = {'aliases': 'q qu', 'category': 'general', 'key': 'quit', 'no_prefix': ' q qu', 'tags': '', 'text': '\n quit when in unlogged-in state\n\n Usage:\n quit\n\n We maintain a different version of the quit command\n here for unconnected accounts for the sake of simplicity. The logged in\n version is a bit more complicated.\n '}¶
Note: Normally this would be tested by importing the ansi parser and run
-the mappings through it. This is not possible since the ansi module creates
-its mapping at the module/class level; since the ansi module is used by so
-many other modules it appears that trying to overload
-settings to test it causes issues with unrelated tests.
Component Attribute Descriptor.
-Allows you to set attributes related to a component on the class.
-It uses AttributeProperty under the hood but prefixes the key with the component name.
Component Attribute Descriptor.
-Allows you to set attributes related to a component on the class.
-It uses AttributeProperty under the hood but prefixes the key with the component name.
Component Attribute Descriptor.
-Allows you to set attributes related to a component on the class.
-It uses AttributeProperty under the hood but prefixes the key with the component name.
Component Attribute Descriptor.
-Allows you to set attributes related to a component on the class.
-It uses AttributeProperty under the hood but prefixes the key with the component name.
Component Tags Descriptor.
-Allows you to set Tags related to a component on the class.
-The tags are set with a prefixed category, so it can support
-multiple tags or enforce a single one.
-
Default value of a tag is added when the component is registered.
-Tags are removed if the component itself is removed.
Component Tags Descriptor.
-Allows you to set Tags related to a component on the class.
-The tags are set with a prefixed category, so it can support
-multiple tags or enforce a single one.
-
Default value of a tag is added when the component is registered.
-Tags are removed if the component itself is removed.
Component Tags Descriptor.
-Allows you to set Tags related to a component on the class.
-The tags are set with a prefixed category, so it can support
-multiple tags or enforce a single one.
-
Default value of a tag is added when the component is registered.
-Tags are removed if the component itself is removed.
Component Tags Descriptor.
-Allows you to set Tags related to a component on the class.
-The tags are set with a prefixed category, so it can support
-multiple tags or enforce a single one.
-
Default value of a tag is added when the component is registered.
-Tags are removed if the component itself is removed.
Component Attribute Descriptor.
-Allows you to set attributes related to a component on the class.
-It uses AttributeProperty under the hood but prefixes the key with the component name.
Component Attribute Descriptor.
-Allows you to set attributes related to a component on the class.
-It uses AttributeProperty under the hood but prefixes the key with the component name.
Component Tags Descriptor.
-Allows you to set Tags related to a component on the class.
-The tags are set with a prefixed category, so it can support
-multiple tags or enforce a single one.
-
Default value of a tag is added when the component is registered.
-Tags are removed if the component itself is removed.
This allows you to register a component on a typeclass.
-Components registered with this property are automatically added
-to any instance of this typeclass.
-
Defaults can be overridden for this typeclass by passing kwargs
This allows you to register a component on a typeclass.
-Components registered with this property are automatically added
-to any instance of this typeclass.
-
Defaults can be overridden for this typeclass by passing kwargs
diff --git a/docs/1.0-dev/api/evennia.contrib.base_systems.email_login.email_login.html b/docs/1.0-dev/api/evennia.contrib.base_systems.email_login.email_login.html
index 9cad1c54ca..a3a4d818ce 100644
--- a/docs/1.0-dev/api/evennia.contrib.base_systems.email_login.email_login.html
+++ b/docs/1.0-dev/api/evennia.contrib.base_systems.email_login.email_login.html
@@ -139,7 +139,7 @@ the module given by settings.CONNECTION_SCREEN_MODULE.
@@ -169,7 +169,7 @@ there is no object yet before the account has logged in)
-search_index_entry = {'aliases': 'co conn con', 'category': 'general', 'key': 'connect', 'no_prefix': ' co conn con', 'tags': '', 'text': '\n Connect to the game.\n\n Usage (at login screen):\n connect <email> <password>\n\n Use the create command to first create an account before logging in.\n '}¶
+search_index_entry = {'aliases': 'con co conn', 'category': 'general', 'key': 'connect', 'no_prefix': ' con co conn', 'tags': '', 'text': '\n Connect to the game.\n\n Usage (at login screen):\n connect <email> <password>\n\n Use the create command to first create an account before logging in.\n '}¶
@@ -191,7 +191,7 @@ there is no object yet before the account has logged in)
@@ -272,7 +272,7 @@ version is a bit more complicated.
-search_index_entry = {'aliases': 'qu q', 'category': 'general', 'key': 'quit', 'no_prefix': ' qu q', 'tags': '', 'text': '\n We maintain a different version of the `quit` command\n here for unconnected accounts for the sake of simplicity. The logged in\n version is a bit more complicated.\n '}¶
+search_index_entry = {'aliases': 'q qu', 'category': 'general', 'key': 'quit', 'no_prefix': ' q qu', 'tags': '', 'text': '\n We maintain a different version of the `quit` command\n here for unconnected accounts for the sake of simplicity. The logged in\n version is a bit more complicated.\n '}¶
@@ -191,7 +191,7 @@ aliases to an already joined channel.
-search_index_entry = {'aliases': 'aliaschan chanalias', 'category': 'comms', 'key': 'addcom', 'no_prefix': ' aliaschan chanalias', 'tags': '', 'text': '\n Add a channel alias and/or subscribe to a channel\n\n Usage:\n addcom [alias=] <channel>\n\n Joins a given channel. If alias is given, this will allow you to\n refer to the channel by this alias rather than the full channel\n name. Subsequent calls of this command can be used to add multiple\n aliases to an already joined channel.\n '}¶
+search_index_entry = {'aliases': 'chanalias aliaschan', 'category': 'comms', 'key': 'addcom', 'no_prefix': ' chanalias aliaschan', 'tags': '', 'text': '\n Add a channel alias and/or subscribe to a channel\n\n Usage:\n addcom [alias=] <channel>\n\n Joins a given channel. If alias is given, this will allow you to\n refer to the channel by this alias rather than the full channel\n name. Subsequent calls of this command can be used to add multiple\n aliases to an already joined channel.\n '}¶
-search_index_entry = {'aliases': 'delaliaschan delchanalias', 'category': 'comms', 'key': 'delcom', 'no_prefix': ' delaliaschan delchanalias', 'tags': '', 'text': "\n remove a channel alias and/or unsubscribe from channel\n\n Usage:\n delcom <alias or channel>\n delcom/all <channel>\n\n If the full channel name is given, unsubscribe from the\n channel. If an alias is given, remove the alias but don't\n unsubscribe. If the 'all' switch is used, remove all aliases\n for that channel.\n "}¶
+search_index_entry = {'aliases': 'delchanalias delaliaschan', 'category': 'comms', 'key': 'delcom', 'no_prefix': ' delchanalias delaliaschan', 'tags': '', 'text': "\n remove a channel alias and/or unsubscribe from channel\n\n Usage:\n delcom <alias or channel>\n delcom/all <channel>\n\n If the full channel name is given, unsubscribe from the\n channel. If an alias is given, remove the alias but don't\n unsubscribe. If the 'all' switch is used, remove all aliases\n for that channel.\n "}¶
diff --git a/docs/1.0-dev/api/evennia.contrib.full_systems.evscaperoom.commands.html b/docs/1.0-dev/api/evennia.contrib.full_systems.evscaperoom.commands.html
index 749e9e463c..856d379672 100644
--- a/docs/1.0-dev/api/evennia.contrib.full_systems.evscaperoom.commands.html
+++ b/docs/1.0-dev/api/evennia.contrib.full_systems.evscaperoom.commands.html
@@ -211,7 +211,7 @@ the operation will be general or on the room.
-search_index_entry = {'aliases': 'abort chicken out quit q', 'category': 'evscaperoom', 'key': 'give up', 'no_prefix': ' abort chicken out quit q', 'tags': '', 'text': '\n Give up\n\n Usage:\n give up\n\n Abandons your attempts at escaping and of ever winning the pie-eating contest.\n\n '}¶
+search_index_entry = {'aliases': 'q quit abort chicken out', 'category': 'evscaperoom', 'key': 'give up', 'no_prefix': ' q quit abort chicken out', 'tags': '', 'text': '\n Give up\n\n Usage:\n give up\n\n Abandons your attempts at escaping and of ever winning the pie-eating contest.\n\n '}¶
-search_index_entry = {'aliases': 'l ls', 'category': 'evscaperoom', 'key': 'look', 'no_prefix': ' l ls', 'tags': '', 'text': '\n Look at the room, an object or the currently focused object\n\n Usage:\n look [obj]\n\n '}¶
+search_index_entry = {'aliases': 'ls l', 'category': 'evscaperoom', 'key': 'look', 'no_prefix': ' ls l', 'tags': '', 'text': '\n Look at the room, an object or the currently focused object\n\n Usage:\n look [obj]\n\n '}¶
-search_index_entry = {'aliases': 'examine unfocus e ex', 'category': 'evscaperoom', 'key': 'focus', 'no_prefix': ' examine unfocus e ex', 'tags': '', 'text': '\n Focus your attention on a target.\n\n Usage:\n focus <obj>\n\n Once focusing on an object, use look to get more information about how it\n looks and what actions is available.\n\n '}¶
+search_index_entry = {'aliases': 'unfocus e examine ex', 'category': 'evscaperoom', 'key': 'focus', 'no_prefix': ' unfocus e examine ex', 'tags': '', 'text': '\n Focus your attention on a target.\n\n Usage:\n focus <obj>\n\n Once focusing on an object, use look to get more information about how it\n looks and what actions is available.\n\n '}¶
-search_index_entry = {'aliases': 'l ls', 'category': 'general', 'key': 'look', 'no_prefix': ' l ls', 'tags': '', 'text': '\n look\n\n Usage:\n look\n look <obj>\n look <room detail>\n look *<account>\n\n Observes your location, details at your location or objects in your vicinity.\n '}¶
+search_index_entry = {'aliases': 'ls l', 'category': 'general', 'key': 'look', 'no_prefix': ' ls l', 'tags': '', 'text': '\n look\n\n Usage:\n look\n look <obj>\n look <room detail>\n look *<account>\n\n Observes your location, details at your location or objects in your vicinity.\n '}¶
Test displaying a part of the map around a central point, showing the [with coord=(0, 0), expectstr=’| n@-’, expectlst=[[‘|’, ‘ ‘], [‘@’, ‘-‘]]]
-character @-symbol in that spot.
Test displaying a part of the map around a central point, showing the [with coord=(1, 0), expectstr=’ |\n-@', expectlst=[[' ', '|’], [‘-’, ‘@’]]]
-character @-symbol in that spot.
Test displaying a part of the map around a central point, showing the [with coord=(0, 1), expectstr=’@-n| ‘, expectlst=[[‘@’, ‘-‘], [‘|’, ‘ ‘]]]
-character @-symbol in that spot.
This is always called whenever this object is initiated –
-that is, whenever it its typeclass is cached from memory. This
-happens on-demand first time the object is used or activated
-in some way after being created but also after each server
-restart or reload.
-search_index_entry = {'aliases': '\' "', 'category': 'general', 'key': 'say', 'no_prefix': ' \' "', 'tags': '', 'text': '\n speak as your character\n\n Usage:\n say <message>\n\n Talk to those in your current location.\n '}¶
+search_index_entry = {'aliases': '" \'', 'category': 'general', 'key': 'say', 'no_prefix': ' " \'', 'tags': '', 'text': '\n speak as your character\n\n Usage:\n say <message>\n\n Talk to those in your current location.\n '}¶
@@ -865,7 +865,7 @@ Using the command without arguments will list all current recogs.
@@ -892,7 +892,7 @@ Using the command without arguments will list all current recogs.
-search_index_entry = {'aliases': 'recognize forget', 'category': 'general', 'key': 'recog', 'no_prefix': ' recognize forget', 'tags': '', 'text': '\n Recognize another person in the same room.\n\n Usage:\n recog\n recog sdesc as alias\n forget alias\n\n Example:\n recog tall man as Griatch\n forget griatch\n\n This will assign a personal alias for a person, or forget said alias.\n Using the command without arguments will list all current recogs.\n\n '}¶
+search_index_entry = {'aliases': 'forget recognize', 'category': 'general', 'key': 'recog', 'no_prefix': ' forget recognize', 'tags': '', 'text': '\n Recognize another person in the same room.\n\n Usage:\n recog\n recog sdesc as alias\n forget alias\n\n Example:\n recog tall man as Griatch\n forget griatch\n\n This will assign a personal alias for a person, or forget said alias.\n Using the command without arguments will list all current recogs.\n\n '}¶
Allows for applying traits as individual properties directly on the parent class
-instead for properties on the .traits handler. So with this you could access data e.g. as
-character.hp.value instead of character.traits.hp.value. This still uses the traitshandler
-under the hood.
Allows for applying traits as individual properties directly on the parent class
-instead for properties on the .traits handler. So with this you could access data e.g. as
-character.hp.value instead of character.traits.hp.value. This still uses the traitshandler
-under the hood.
Allows for applying traits as individual properties directly on the parent class
-instead for properties on the .traits handler. So with this you could access data e.g. as
-character.hp.value instead of character.traits.hp.value. This still uses the traitshandler
-under the hood.
Allows for applying traits as individual properties directly on the parent class
-instead for properties on the .traits handler. So with this you could access data e.g. as
-character.hp.value instead of character.traits.hp.value. This still uses the traitshandler
-under the hood.
diff --git a/docs/1.0-dev/api/evennia.contrib.tutorials.red_button.red_button.html b/docs/1.0-dev/api/evennia.contrib.tutorials.red_button.red_button.html
index 9e6fff621d..a6ed338b0f 100644
--- a/docs/1.0-dev/api/evennia.contrib.tutorials.red_button.red_button.html
+++ b/docs/1.0-dev/api/evennia.contrib.tutorials.red_button.red_button.html
@@ -153,7 +153,7 @@ such as when closing the lid and un-blinding a character.
-search_index_entry = {'aliases': 'l get listen ex examine feel', 'category': 'general', 'key': 'look', 'no_prefix': ' l get listen ex examine feel', 'tags': '', 'text': "\n Looking around in darkness\n\n Usage:\n look <obj>\n\n ... not that there's much to see in the dark.\n\n "}¶
+search_index_entry = {'aliases': 'l ex examine listen get feel', 'category': 'general', 'key': 'look', 'no_prefix': ' l ex examine listen get feel', 'tags': '', 'text': "\n Looking around in darkness\n\n Usage:\n look <obj>\n\n ... not that there's much to see in the dark.\n\n "}¶
diff --git a/docs/1.0-dev/api/evennia.contrib.tutorials.tutorial_world.objects.html b/docs/1.0-dev/api/evennia.contrib.tutorials.tutorial_world.objects.html
index 5150b7e3f9..2c2233437c 100644
--- a/docs/1.0-dev/api/evennia.contrib.tutorials.tutorial_world.objects.html
+++ b/docs/1.0-dev/api/evennia.contrib.tutorials.tutorial_world.objects.html
@@ -425,7 +425,7 @@ of the object. We overload it with our own version.
@@ -805,7 +805,7 @@ parry - forgoes your attack but will make you harder to hit on next
-search_index_entry = {'aliases': 'kill slash thrust bash fight parry pierce defend hit chop stab', 'category': 'tutorialworld', 'key': 'attack', 'no_prefix': ' kill slash thrust bash fight parry pierce defend hit chop stab', 'tags': '', 'text': '\n Attack the enemy. Commands:\n\n stab <enemy>\n slash <enemy>\n parry\n\n stab - (thrust) makes a lot of damage but is harder to hit with.\n slash - is easier to land, but does not make as much damage.\n parry - forgoes your attack but will make you harder to hit on next\n enemy attack.\n\n '}¶
+search_index_entry = {'aliases': 'thrust pierce bash hit chop stab parry defend kill slash fight', 'category': 'tutorialworld', 'key': 'attack', 'no_prefix': ' thrust pierce bash hit chop stab parry defend kill slash fight', 'tags': '', 'text': '\n Attack the enemy. Commands:\n\n stab <enemy>\n slash <enemy>\n parry\n\n stab - (thrust) makes a lot of damage but is harder to hit with.\n slash - is easier to land, but does not make as much damage.\n parry - forgoes your attack but will make you harder to hit on next\n enemy attack.\n\n '}¶
diff --git a/docs/1.0-dev/api/evennia.contrib.tutorials.tutorial_world.rooms.html b/docs/1.0-dev/api/evennia.contrib.tutorials.tutorial_world.rooms.html
index 8aefd2c8ee..8260f7e70e 100644
--- a/docs/1.0-dev/api/evennia.contrib.tutorials.tutorial_world.rooms.html
+++ b/docs/1.0-dev/api/evennia.contrib.tutorials.tutorial_world.rooms.html
@@ -248,7 +248,7 @@ code except for adding in the details.
@@ -263,7 +263,7 @@ code except for adding in the details.
-search_index_entry = {'aliases': 'l ls', 'category': 'tutorialworld', 'key': 'look', 'no_prefix': ' l ls', 'tags': '', 'text': '\n looks at the room and on details\n\n Usage:\n look <obj>\n look <room detail>\n look *<account>\n\n Observes your location, details at your location or objects\n in your vicinity.\n\n Tutorial: This is a child of the default Look command, that also\n allows us to look at "details" in the room. These details are\n things to examine and offers some extra description without\n actually having to be actual database objects. It uses the\n return_detail() hook on TutorialRooms for this.\n '}¶
+search_index_entry = {'aliases': 'ls l', 'category': 'tutorialworld', 'key': 'look', 'no_prefix': ' ls l', 'tags': '', 'text': '\n looks at the room and on details\n\n Usage:\n look <obj>\n look <room detail>\n look *<account>\n\n Observes your location, details at your location or objects\n in your vicinity.\n\n Tutorial: This is a child of the default Look command, that also\n allows us to look at "details" in the room. These details are\n things to examine and offers some extra description without\n actually having to be actual database objects. It uses the\n return_detail() hook on TutorialRooms for this.\n '}¶
@@ -996,7 +996,7 @@ random chance of eventually finding a light source.
-search_index_entry = {'aliases': 'l feel feel around search fiddle', 'category': 'tutorialworld', 'key': 'look', 'no_prefix': ' l feel feel around search fiddle', 'tags': '', 'text': '\n Look around in darkness\n\n Usage:\n look\n\n Look around in the darkness, trying\n to find something.\n '}¶
+search_index_entry = {'aliases': 'fiddle feel around l feel search', 'category': 'tutorialworld', 'key': 'look', 'no_prefix': ' fiddle feel around l feel search', 'tags': '', 'text': '\n Look around in darkness\n\n Usage:\n look\n\n Look around in the darkness, trying\n to find something.\n '}¶
-search_index_entry = {'aliases': 'y no a __nomatch_command abort n yes', 'category': 'general', 'key': '__noinput_command', 'no_prefix': ' y no a __nomatch_command abort n yes', 'tags': '', 'text': '\n Handle a prompt for yes or no. Press [return] for the default choice.\n\n '}¶
+search_index_entry = {'aliases': '__nomatch_command abort a no y yes n', 'category': 'general', 'key': '__noinput_command', 'no_prefix': ' __nomatch_command abort a no y yes n', 'tags': '', 'text': '\n Handle a prompt for yes or no. Press [return] for the default choice.\n\n '}¶
diff --git a/docs/1.0-dev/api/evennia.utils.evmore.html b/docs/1.0-dev/api/evennia.utils.evmore.html
index ca0db63bc9..758da29859 100644
--- a/docs/1.0-dev/api/evennia.utils.evmore.html
+++ b/docs/1.0-dev/api/evennia.utils.evmore.html
@@ -137,7 +137,7 @@ the caller.msg() construct every time the page is updated.
@@ -163,7 +163,7 @@ the caller.msg() construct every time the page is updated.
-search_index_entry = {'aliases': 'p e t next a previous top q end abort quit n', 'category': 'general', 'key': '__noinput_command', 'no_prefix': ' p e t next a previous top q end abort quit n', 'tags': '', 'text': '\n Manipulate the text paging. Catch no-input with aliases.\n '}¶
+search_index_entry = {'aliases': 'previous abort q next end a quit p top t e n', 'category': 'general', 'key': '__noinput_command', 'no_prefix': ' previous abort q next end a quit p top t e n', 'tags': '', 'text': '\n Manipulate the text paging. Catch no-input with aliases.\n '}¶
BaseEvenniaTestCase - no default objects, only enforced default settings
-
BaseEvenniaTest - all default objects, enforced default settings
-
BaseEvenniaCommandTest - for testing Commands, enforced default settings
-
-
Classes for testing game folder content:
-
-
-
EvenniaTestCase - no default objects, using gamedir settings (identical to
standard Python TestCase)
-
-
-
-
EvenniaTest - all default objects, using gamedir settings
-
EvenniaCommandTest - for testing game folder commands, using gamedir settings
-
-
Other:
-
-
EvenniaTestMixin - A class mixin for creating the test environment objects, for
-making custom tests.
-
EvenniaCommandMixin - A class mixin that adds support for command testing with the .call()
-helper. Used by the command-test classes, but can be used for making a customt test class.
module (module, object or str) – The module will
-be removed so it will have to be imported again. If given
-an object, the module in which that object sits will be unloaded. A string
-should directly give the module pathname to unload.
-
-
-
Example
-
# (in a test method)
-unload_module(foo)
-withmock.patch("foo.GLOBALTHING","mockval"):
- importfoo
- ...# test code using foo.GLOBALTHING, now set to 'mockval'
-
-
-
This allows for mocking constants global to the module, since
-otherwise those would not be mocked (since a module is only
-loaded once).
Mixin to add to a test in order to provide the .call helper for
-testing the execution and returns of a command.
-
Tests a Command by running it and comparing what messages it sends with
-expected values. This tests without actually spinning up the cmdhandler
-for every test, which is more controlled.
-
Example:
-
fromcommands.echoimportCmdEcho
-
-classMyCommandTest(EvenniaTest,CommandTestMixin):
-
- deftest_echo(self):
- '''
- Test that the echo command really returns
- what you pass into it.
- '''
- self.call(MyCommand(),"hello world!",
- "You hear your echo: 'Hello world!'")
-
Test a command by assigning all the needed properties to a cmdobj and
-running the sequence. The resulting .msg calls will be mocked and
-the text= calls to them compared to a expected output.
input_args (str) – This should be the full input the Command should
-see, such as ‘look here’. This will become .args for the Command
-instance to parse.
-
msg (str or dict, optional) – This is the expected return value(s)
-returned through caller.msg(text=…) calls in the command. If a string, the
-receiver is controlled with the receiver kwarg (defaults to caller).
-If this is a dict, it is a mapping
-{receiver1: “expected1”, receiver2: “expected2”,…} and receiver is
-ignored. The message(s) are compared with the actual messages returned
-to the receiver(s) as the Command runs. Each check uses .startswith,
-so you can choose to only include the first part of the
-returned message if that’s enough to verify a correct result. EvMenu
-decorations (like borders) are stripped and should not be included. This
-should also not include color tags unless noansi=False.
-If the command returns texts in multiple separate .msg-
-calls to a receiver, separate these with | if noansi=True
-(default) and || if noansi=False. If no msg is given (None),
-then no automatic comparison will be done.
-
cmdset (str, optional) – If given, make .cmdset available on the Command
-instance as it runs. While .cmdset is normally available on the
-Command instance by default, this is usually only used by
-commands that explicitly operates/displays cmdsets, like
-examine.
-
noansi (str, optional) – By default the color tags of the msg is
-ignored, this makes them significant. If unset, msg must contain
-the same color tags as the actual return message.
-
caller (Object or Account, optional) – By default self.char1 is used as the
-command-caller (the .caller property on the Command). This allows to
-execute with another caller, most commonly an Account.
-
receiver (Object or Account, optional) – This is the object to receive the
-return messages we want to test. By default this is the same as caller
-(which in turn defaults to is self.char1). Note that if msg is
-a dict, this is ignored since the receiver is already specified there.
-
cmdstring (str, optional) – Normally this is the Command’s key.
-This allows for tweaking the .cmdname property of the
-Command**. This isb used for commands with multiple aliases,
-where the command explicitly checs which alias was used to
-determine its functionality.
-
obj (str, optional) – This sets the .obj property of the Command - the
-object on which the Command ‘sits’. By default this is the same as caller.
-This can be used for testing on-object Command interactions.
-
inputs (list, optional) – A list of strings to pass to functions that pause to
-take input from the user (normally using @interactive and
-ret = yield(question) or evmenu.get_input). Each element of the
-list will be passed into the command as if the user answered each prompt
-in that order.
-
raw_string (str, optional) – Normally the .raw_string property is set as
-a combination of your key/cmdname and input_args. This allows
-direct control of what this is, for example for testing edge cases
-or malformed inputs.
-
-
-
Returns
-
str or dict –
-
-
The message sent to receiver, or a dict of
{receiver: “msg”, …} if multiple are given. This is usually
-only used with msg=None to do the validation externally.
-
-
-
-
-
Raises
-
AssertionError – If the returns of .msg calls (tested with .startswith) does not
-match expected_input.
-
-
-
Notes
-
As part of the tests, all methods of the Command will be called in
-the proper order:
This test class is intended for inheriting in mygame tests.
-It helps ensure your tests are run with your own objects
-and settings from your game folder.
The use of this module by the funcparser expects a default person-pronoun [with pronoun=’you’, options=’p’, expected_1st_or_2nd_person=’you’, expected_3rd_person=’they’]
+of the neutral “they”, which is categorized here by the plural.
The use of this module by the funcparser expects a default person-pronoun [with pronoun=’I’, options=’p’, expected_1st_or_2nd_person=’I’, expected_3rd_person=’they’]
+of the neutral “they”, which is categorized here by the plural.
The use of this module by the funcparser expects a default person-pronoun [with pronoun=’Me’, options=’p’, expected_1st_or_2nd_person=’Me’, expected_3rd_person=’Them’]
+of the neutral “they”, which is categorized here by the plural.
The use of this module by the funcparser expects a default person-pronoun [with pronoun=’your’, options=’p’, expected_1st_or_2nd_person=’your’, expected_3rd_person=’their’]
+of the neutral “they”, which is categorized here by the plural.
The use of this module by the funcparser expects a default person-pronoun [with pronoun=’they’, options=’1 p’, expected_1st_or_2nd_person=’we’, expected_3rd_person=’they’]
+of the neutral “they”, which is categorized here by the plural.
The use of this module by the funcparser expects a default person-pronoun [with pronoun=’they’, options=’’, expected_1st_or_2nd_person=’you’, expected_3rd_person=’they’]
+of the neutral “they”, which is categorized here by the plural.
The use of this module by the funcparser expects a default person-pronoun [with pronoun=’yourself’, options=’p’, expected_1st_or_2nd_person=’yourself’, expected_3rd_person=’themselves’]
+of the neutral “they”, which is categorized here by the plural.
The use of this module by the funcparser expects a default person-pronoun [with pronoun=’myself’, options=’p’, expected_1st_or_2nd_person=’myself’, expected_3rd_person=’themselves’]
+of the neutral “they”, which is categorized here by the plural.
diff --git a/docs/1.0-dev/genindex.html b/docs/1.0-dev/genindex.html
index 0469bb5486..d84a2fd667 100644
--- a/docs/1.0-dev/genindex.html
+++ b/docs/1.0-dev/genindex.html
@@ -103,7 +103,6 @@
| W
| X
| Y
- | Z
diff --git a/docs/1.0-dev/index.html b/docs/1.0-dev/index.html
index 13158fa03c..aee86e5ffa 100644
--- a/docs/1.0-dev/index.html
+++ b/docs/1.0-dev/index.html
@@ -154,7 +154,7 @@ or the original github wiki. You have been warned.