Reorganize docs into flat folder layout

This commit is contained in:
Griatch 2020-06-17 18:06:41 +02:00
parent 106558cec0
commit 892d8efb93
135 changed files with 34 additions and 1180 deletions

View file

@ -0,0 +1,222 @@
# Continuous Integration
One of the advantages of Evennia over traditional MUSH development systems is that Evennia is
capable of integrating into enterprise level integration environments and source control. Because of
this, it can also be the subject of automation for additional convenience, allowing a more
streamlined development environment.
## What is Continuous Integration?
[Continuous Integration (CI)](https://www.thoughtworks.com/continuous-integration) is a development
practice that requires developers to integrate code into a shared repository several times a day.
Each check-in is then verified by an automated build, allowing teams to detect problems early.
For Evennia, continuous integration allows an automated build process to:
* Pull down a latest build from Source Control.
* Run migrations on the backing SQL database.
* Automate additional unique tasks for that project.
* Run unit tests.
* Publish those files to the server directory
* Reload the game.
## Preparation
To prepare a CI environment for your `MU*`, it will be necessary to set up some prerequisite
software for your server.
Among those you will need:
* A Continuous Integration Environment.
* I recommend [TeamCity](https://www.jetbrains.com/teamcity/) which has an in-depth [Setup
Guide](https://confluence.jetbrains.com/display/TCD8/Installing+and+Configuring+the+TeamCity+Server)
* [Source Control](Version-Control)
* This could be Git or SVN or any other available SC.
## Linux TeamCity Setup
For this part of the guide, an example setup will be provided for administrators running a TeamCity
build integration environment on Linux.
After meeting the preparation steps for your specific environment, log on to your teamcity interface
at `http://<your server>:8111/`.
Create a new project named "Evennia" and in it construct a new template called continuous-
integration.
### A Quick Overview
Templates are fancy objects in TeamCity that allow an administrator to define build steps that are
shared between one or more build projects. Assigning a VCS Root (Source Control) is unnecessary at
this stage, primarily you'll be worrying about the build steps and your default parameters (both
visible on the tabs to the left.)
### Template Setup
In this template, you'll be outlining the steps necessary to build your specific game. (A number of
sample scripts are provided under this section below!) Click Build Steps and prepare your general
flow. For this example, we will be doing a few basic example steps:
* Transforming the Settings.py file
* We do this to update ports or other information that make your production environment unique
from your development environment.
* Making migrations and migrating the game database.
* Publishing the game files.
* Reloading the server.
For each step we'll being use the "Command Line Runner" (a fancy name for a shell script executor).
* Create a build step with the name: Transform Configuration
* For the script add:
```bash
#!/bin/bash
# Replaces the game configuration with one
# appropriate for this deployment.
CONFIG="%system.teamcity.build.checkoutDir%/server/conf/settings.py"
MYCONF="%system.teamcity.build.checkoutDir%/server/conf/my.cnf"
sed -e 's/TELNET_PORTS = [4000]/TELNET_PORTS = [%game.ports%]/g' "$CONFIG" > "$CONFIG".tmp && mv
"$CONFIG".tmp "$CONFIG"
sed -e 's/WEBSERVER_PORTS = [(4001, 4002)]/WEBSERVER_PORTS = [%game.webports%]/g' "$CONFIG" >
"$CONFIG".tmp && mv "$CONFIG".tmp "$CONFIG"
# settings.py MySQL DB configuration
echo Configuring Game Database...
echo "" >> "$CONFIG"
echo "######################################################################" >> "$CONFIG"
echo "# MySQL Database Configuration" >> "$CONFIG"
echo "######################################################################" >> "$CONFIG"
echo "DATABASES = {" >> "$CONFIG"
echo " 'default': {" >> "$CONFIG"
echo " 'ENGINE': 'django.db.backends.mysql'," >> "$CONFIG"
echo " 'OPTIONS': {" >> "$CONFIG"
echo " 'read_default_file': 'server/conf/my.cnf'," >> "$CONFIG"
echo " }," >> "$CONFIG"
echo " }" >> "$CONFIG"
echo "}" >> "$CONFIG"
# Create the My.CNF file.
echo "[client]" >> "$MYCONF"
echo "database = %mysql.db%" >> "$MYCONF"
echo "user = %mysql.user%" >> "$MYCONF"
echo "password = %mysql.pass%" >> "$MYCONF"
echo "default-character-set = utf8" >> "$MYCONF"
```
If you look at the parameters side of the page after saving this script, you'll notice that some new
parameters have been populated for you. This is because we've included new teamcity configuration
parameters that are populated when the build itself is ran. When creating projects that inherit this
template, we'll be able to fill in or override those parameters for project-specific configuration.
* Go ahead and create another build step called "Make Database Migration"
* If you're using SQLLite on your game, it will be prudent to change working directory on this
step to: %game.dir%
* In this script include:
```bash
#!/bin/bash
# Update the DB migration
LOGDIR="server/logs"
. %evenv.dir%/bin/activate
# Check that the logs directory exists.
if [ ! -d "$LOGDIR" ]; then
# Control will enter here if $LOGDIR doesn't exist.
mkdir "$LOGDIR"
fi
evennia makemigrations
```
* Create yet another build step, this time named: "Execute Database Migration":
* If you're using SQLLite on your game, it will be prudent to change working directory on this
step to: %game.dir%
```bash
#!/bin/bash
# Apply the database migration.
LOGDIR="server/logs"
. %evenv.dir%/bin/activate
# Check that the logs directory exists.
if [ ! -d "$LOGDIR" ]; then
# Control will enter here if $LOGDIR doesn't exist.
mkdir "$LOGDIR"
fi
evennia migrate
```
Our next build step is where we actually publish our build. Up until now, all work on game has been
done in a 'work' directory on TeamCity's build agent. From that directory we will now copy our files
to where our game actually exists on the local server.
* Create a new build step called "Publish Build":
* If you're using SQLLite on your game, be sure to order this step ABOVE the Database Migration
steps. The build order will matter!
```bash
#!/bin/bash
# Publishes the build to the proper build directory.
DIRECTORY="%game.dir%"
if [ ! -d "$DIRECTORY" ]; then
# Control will enter here if $DIRECTORY doesn't exist.
mkdir "$DIRECTORY"
fi
# Copy all the files.
cp -ruv %teamcity.build.checkoutDir%/* "$DIRECTORY"
chmod -R 775 "$DIRECTORY"
```
Finally the last script will reload our game for us.
* Create a new script called "Reload Game":
* The working directory on this build step will be: %game.dir%
```bash
#!/bin/bash
# Apply the database migration.
LOGDIR="server/logs"
PIDDIR="server/server.pid"
. %evenv.dir%/bin/activate
# Check that the logs directory exists.
if [ ! -d "$LOGDIR" ]; then
# Control will enter here if $LOGDIR doesn't exist.
mkdir "$LOGDIR"
fi
# Check that the server is running.
if [ -d "$PIDDIR" ]; then
# Control will enter here if the game is running.
evennia reload
fi
```
Now the template is ready for use! It would be useful this time to revisit the parameters page and
set the evenv parameter to the directory where your virtualenv exists: IE "/srv/mush/evenv".
### Creating the Project
Now it's time for the last few steps to set up a CI environment.
* Return to the Evennia Project overview/administration page.
* Create a new Sub-Project called "Production"
* This will be the category that holds our actual game.
* Create a new Build Configuration in Production with the name of your MUSH.
* Base this configuration off of the continuous-integration template we made earlier.
* In the build configuration, enter VCS roots and create a new VCS root that points to the
branch/version control that you are using.
* Go to the parameters page and fill in the undefined parameters for your specific configuration.
* If you wish for the CI to run every time a commit is made, go to the VCS triggers and add one for
"On Every Commit".
And you're done! At this point, you can return to the project overview page and queue a new build
for your game. If everything was set up correctly, the build will complete successfully. Additional
build steps could be added or removed at this point, adding some features like Unit Testing or more!

View file

@ -0,0 +1,296 @@
# Debugging
Sometimes, an error is not trivial to resolve. A few simple `print` statements is not enough to find
the cause of the issue. Running a *debugger* can then be very helpful and save a lot of time.
Debugging
means running Evennia under control of a special *debugger* program. This allows you to stop the
action at a given point, view the current state and step forward through the program to see how its
logic works.
Evennia natively supports these debuggers:
- [Pdb](https://docs.python.org/2/library/pdb.html) is a part of the Python distribution and
available out-of-the-box.
- [PuDB](https://pypi.org/project/pudb/) is a third-party debugger that has a slightly more
'graphical', curses-based user interface than pdb. It is installed with `pip install pudb`.
## Debugging Evennia
To run Evennia with the debugger, follow these steps:
1. Find the point in the code where you want to have more insight. Add the following line at that
point.
```python
from evennia import set_trace;set_trace()
```
2. (Re-)start Evennia in interactive (foreground) mode with `evennia istart`. This is important -
without this step the debugger will not start correctly - it will start in this interactive
terminal.
3. Perform the steps that will trigger the line where you added the `set_trace()` call. The debugger
will start in the terminal from which Evennia was interactively started.
The `evennia.set_trace` function takes the following arguments:
```python
evennia.set_trace(debugger='auto', term_size=(140, 40))
```
Here, `debugger` is one of `pdb`, `pudb` or `auto`. If `auto`, use `pudb` if available, otherwise
use `pdb`. The `term_size` tuple sets the viewport size for `pudb` only (it's ignored by `pdb`).
## A simple example using pdb
The debugger is useful in different cases, but to begin with, let's see it working in a command.
Add the following test command (which has a range of deliberate errors) and also add it to your
default cmdset. Then restart Evennia in interactive mode with `evennia istart`.
```python
# In file commands/command.py
class CmdTest(Command):
"""
A test command just to test pdb.
Usage:
test
"""
key = "test"
def func(self):
from evennia import set_trace; set_trace() # <--- start of debugger
obj = self.search(self.args)
self.msg("You've found {}.".format(obj.get_display_name()))
```
If you type `test` in your game, everything will freeze. You won't get any feedback from the game,
and you won't be able to enter any command (nor anyone else). It's because the debugger has started
in your console, and you will find it here. Below is an example with `pdb`.
```
...
> .../mygame/commands/command.py(79)func()
-> obj = self.search(self.args)
(Pdb)
```
`pdb` notes where it has stopped execution and, what line is about to be executed (in our case, `obj
= self.search(self.args)`), and ask what you would like to do.
### Listing surrounding lines of code
When you have the `pdb` prompt `(Pdb)`, you can type in different commands to explore the code. The
first one you should know is `list` (you can type `l` for short):
```
(Pdb) l
43
44 key = "test"
45
46 def func(self):
47 from evennia import set_trace; set_trace() # <--- start of debugger
48 -> obj = self.search(self.args)
49 self.msg("You've found {}.".format(obj.get_display_name()))
50
51 # -------------------------------------------------------------
52 #
53 # The default commands inherit from
(Pdb)
```
Okay, this didn't do anything spectacular, but when you become more confident with `pdb` and find
yourself in lots of different files, you sometimes need to see what's around in code. Notice that
there is a little arrow (`->`) before the line that is about to be executed.
This is important: **about to be**, not **has just been**. You need to tell `pdb` to go on (we'll
soon see how).
### Examining variables
`pdb` allows you to examine variables (or really, to run any Python instruction). It is very useful
to know the values of variables at a specific line. To see a variable, just type its name (as if
you were in the Python interpreter:
```
(Pdb) self
<commands.command.CmdTest object at 0x045A0990>
(Pdb) self.args
u''
(Pdb) self.caller
<Character: XXX>
(Pdb)
```
If you try to see the variable `obj`, you'll get an error:
```
(Pdb) obj
*** NameError: name 'obj' is not defined
(Pdb)
```
That figures, since at this point, we haven't created the variable yet.
> Examining variable in this way is quite powerful. You can even run Python code and keep on
> executing, which can help to check that your fix is actually working when you have identified an
> error. If you have variable names that will conflict with `pdb` commands (like a `list`
> variable), you can prefix your variable with `!`, to tell `pdb` that what follows is Python code.
### Executing the current line
It's time we asked `pdb` to execute the current line. To do so, use the `next` command. You can
shorten it by just typing `n`:
```
(Pdb) n
AttributeError: "'CmdTest' object has no attribute 'search'"
> .../mygame/commands/command.py(79)func()
-> obj = self.search(self.args)
(Pdb)
```
`Pdb` is complaining that you try to call the `search` method on a command... whereas there's no
`search` method on commands. The character executing the command is in `self.caller`, so we might
change our line:
```python
obj = self.caller.search(self.args)
```
### Letting the program run
`pdb` is waiting to execute the same instruction... it provoked an error but it's ready to try
again, just in case. We have fixed it in theory, but we need to reload, so we need to enter a
command. To tell `pdb` to terminate and keep on running the program, use the `continue` (or `c`)
command:
```
(Pdb) c
...
```
You see an error being caught, that's the error we have fixed... or hope to have. Let's reload the
game and try again. You need to run `evennia istart` again and then run `test` to get into the
command again.
```
> .../mygame/commands/command.py(79)func()
-> obj = self.caller.search(self.args)
(Pdb)
```
`pdb` is about to run the line again.
```
(Pdb) n
> .../mygame/commands/command.py(80)func()
-> self.msg("You've found {}.".format(obj.get_display_name()))
(Pdb)
```
This time the line ran without error. Let's see what is in the `obj` variable:
```
(Pdb) obj
(Pdb) print obj
None
(Pdb)
```
We have entered the `test` command without parameter, so no object could be found in the search
(`self.args` is an empty string).
Let's allow the command to continue and try to use an object name as parameter (although, we should
fix that bug too, it would be better):
```
(Pdb) c
...
```
Notice that you'll have an error in the game this time. Let's try with a valid parameter. I have
another character, `barkeep`, in this room:
```test barkeep```
And again, the command freezes, and we have the debugger opened in the console.
Let's execute this line right away:
```
> .../mygame/commands/command.py(79)func()
-> obj = self.caller.search(self.args)
(Pdb) n
> .../mygame/commands/command.py(80)func()
-> self.msg("You've found {}.".format(obj.get_display_name()))
(Pdb) obj
<Character: barkeep>
(Pdb)
```
At least this time we have found the object. Let's process...
```
(Pdb) n
TypeError: 'get_display_name() takes exactly 2 arguments (1 given)'
> .../mygame/commands/command.py(80)func()
-> self.msg("You've found {}.".format(obj.get_display_name()))
(Pdb)
```
As an exercise, fix this error, reload and run the debugger again. Nothing better than some
experimenting!
Your debugging will often follow the same strategy:
1. Receive an error you don't understand.
2. Put a breaking point **BEFORE** the error occurs.
3. Run the code again and see the debugger open.
4. Run the program line by line,examining variables, checking the logic of instructions.
5. Continue and try again, each step a bit further toward the truth and the working feature.
### Stepping through a function
`n` is useful, but it will avoid stepping inside of functions if it can. But most of the time, when
we have an error we don't understand, it's because we use functions or methods in a way that wasn't
intended by the developer of the API. Perhaps using wrong arguments, or calling the function in a
situation that would cause a bug. When we have a line in the debugger that calls a function or
method, we can "step" to examine it further. For instance, in the previous example, when `pdb` was
about to execute `obj = self.caller.search(self.args)`, we may want to see what happens inside of
the `search` method.
To do so, use the `step` (or `s`) command. This command will show you the definition of the
function/method and you can then use `n` as before to see it line-by-line. In our little example,
stepping through a function or method isn't that useful, but when you have an impressive set of
commands, functions and so on, it might really be handy to examine some feature and make sure they
operate as planned.
## Cheat-sheet of pdb/pudb commands
PuDB and Pdb share the same commands. The only real difference is how it's presented. The `look`
command is not needed much in `pudb` since it displays the code directly in its user interface.
| Pdb/PuDB command | To do what |
| ----------- | ---------- |
| list (or l) | List the lines around the point of execution (not needed for `pudb`, it will show
this directly). |
| print (or p) | Display one or several variables. |
| `!` | Run Python code (using a `!` is often optional). |
| continue (or c) | Continue execution and terminate the debugger for this time. |
| next (or n) | Execute the current line and goes to the next one. |
| step (or s) | Step inside of a function or method to examine it. |
| `<RETURN>` | Repeat the last command (don't type `n` repeatedly, just type it once and then press
`<RETURN>` to repeat it). |
If you want to learn more about debugging with Pdb, you will find an [interesting tutorial on that
topic here](https://pymotw.com/3/pdb/).

View file

@ -0,0 +1,68 @@
# Directory Overview
This is an overview of the directories relevant to Evennia coding.
## The Game directory
The game directory is created with `evennia --init <name>`. In the Evennia documentation we always
assume it's called `mygame`. Apart from the `server/` subfolder within, you could reorganize this
folder if you preferred a different code structure for your game.
- `mygame/`
- `commands/` - Overload default [Commands](Commands) or add your own Commands/[Command
sets](Command-Sets) here.
- `server`/ - The structure of this folder should not change since Evennia expects it.
- [`conf/`](https://github.com/evennia/evennia/tree/master/evennia/game_template/server) - All
server configuration files sits here. The most important file is `settings.py`.
- `logs/` - Portal log files are stored here (Server is logging to the terminal by default)
- `typeclasses/` - this folder contains empty templates for overloading default game entities of
Evennia. Evennia will automatically use the changes in those templates for the game entities it
creates.
- `web/` - This holds the [Web features](Web-Features) of your game.
- `world/` - this is a "miscellaneous" folder holding everything related to the world you are
building, such as build scripts and rules modules that don't fit with one of the other folders.
## Evennia library layout:
If you cloned the GIT repo following the instructions, you will have a folder named `evennia`. The
top level of it contains Python package specific stuff such as a readme file, `setup.py` etc. It
also has two subfolders`bin/` and `evennia/` (again).
The `bin/` directory holds OS-specific binaries that will be used when installing Evennia with `pip`
as per the [Getting started](Getting-Started) instructions. The library itself is in the `evennia`
subfolder. From your code you will access this subfolder simply by `import evennia`.
- evennia
- [`__init__.py`](Evennia-API) - The "flat API" of Evennia resides here.
- [`commands/`](Commands) - The command parser and handler.
- `default/` - The [default commands](Default-Command-Help) and cmdsets.
- [`comms/`](Communications) - Systems for communicating in-game.
- `contrib/` - Optional plugins too game-specific for core Evennia.
- `game_template/` - Copied to become the "game directory" when using `evennia --init`.
- [`help/`](Help-System) - Handles the storage and creation of help entries.
- `locale/` - Language files ([i18n](Internationalization)).
- [`locks/`](Locks) - Lock system for restricting access to in-game entities.
- [`objects/`](Objects) - In-game entities (all types of items and Characters).
- [`prototypes/`](Spawner-and-Prototypes) - Object Prototype/spawning system and OLC menu
- [`accounts/`](Accounts) - Out-of-game Session-controlled entities (accounts, bots etc)
- [`scripts/`](Scripts) - Out-of-game entities equivalence to Objects, also with timer support.
- [`server/`](Portal-And-Server) - Core server code and Session handling.
- `portal/` - Portal proxy and connection protocols.
- [`settings_default.py`](Server-Conf#Settings-file) - Root settings of Evennia. Copy settings
from here to `mygame/server/settings.py` file.
- [`typeclasses/`](Typeclasses) - Abstract classes for the typeclass storage and database system.
- [`utils/`](Coding-Utils) - Various miscellaneous useful coding resources.
- [`web/`](Web-Features) - Web resources and webserver. Partly copied into game directory on
initialization.
All directories contain files ending in `.py`. These are Python *modules* and are the basic units of
Python code. The roots of directories also have (usually empty) files named `__init__.py`. These are
required by Python so as to be able to find and import modules in other directories. When you have
run Evennia at least once you will find that there will also be `.pyc` files appearing, these are
pre-compiled binary versions of the `.py` files to speed up execution.
The root of the `evennia` folder has an `__init__.py` file containing the "[flat API](Evennia-API)".
This holds shortcuts to various subfolders in the evennia library. It is provided to make it easier
to find things; it allows you to just import `evennia` and access things from that rather than
having to import from their actual locations inside the source tree.

View file

@ -0,0 +1,77 @@
# Evennia API
Evennia makes much of its programming tools available directly from the top-level `evennia` package.
This is often referred to as Evennia's "flat" [Application Programming
Interface](https://en.wikipedia.org/wiki/Application_programming_interface) (API). The flat API
tries to collect and bring the most commonly used resources to the front in a way where everything
is available at a glance (in a flat display), making it a good place to start to learn Evennia.
> Evennia's flat (and full) API can be perused through the auto-generated [API Library
refence](github:evennia).
A good, interactive way to explore the flat API is to use [IPython](http://ipython.org/), a more
flexible version of the default Python shell. Inside your virtual environment you can install
IPython simply by
pip install ipython
Windows users should also install [PyReadline](http://ipython.org/pyreadline.html):
pip install pyreadline
With IPython installed, go to your game directory and run
evennia shell
This should give you the IPython shell automatically. Inside IPython
you then do
import evennia
Followed by
evennia.<TAB>
That is, write `evennia.` and press the TAB key. What pops up is the contents of the `evennia` top-
level package - in other words [the "flat" API](github:evennia#the-flat-api).
evennia.DefaultObject?
Starting to write the name of an API entity and pressing `<TAB>` will auto-complete the name. Adding
a question mark (`?`) to its name will show you its documentation. Append `??` to get the actual
source code. This way you can quickly explore Evennia and see what is available.
## To remember when importing from `evennia`
Properties on the root of the `evennia` package are *not* modules in their own right. They are just
shortcut properties stored in the `evennia/__init__.py` module. That means that you cannot use dot-
notation to `import` nested module-names over `evennia`. The rule of thumb is that you cannot use
`import` for more than one level down. Hence you can do
```python
import evennia
print(evennia.default_cmds.CmdLook)
```
or import one level down
```python
from evennia import default_cmds
print(default_cmds.CmdLook)
```
but you *cannot* import two levels down
```python
from evennia.default_cmds import CmdLook # error!
```
This will give you an `ImportError` telling you that the module `default_cmds` cannot be found -
this is becasue `default_cmds` is just a *variable* stored in `evennia.__init__.py`; this cannot be
imported from. If you really want full control over which level of package you import you can always
bypass the root package and import directly from from the real location. For example
`evennia.DefaultObject` is a shortcut to `evennia.objects.objects.DefaultObject`. Using this full
path will have the import mechanism work normally. See `evennia/__init__.py` to see where the
package imports from.

View file

@ -0,0 +1,125 @@
# Profiling
*This is considered an advanced topic mainly of interest to server developers.*
## Introduction
Sometimes it can be useful to try to determine just how efficient a particular piece of code is, or
to figure out if one could speed up things more than they are. There are many ways to test the
performance of Python and the running server.
Before digging into this section, remember Donald Knuth's [words of
wisdom](https://en.wikipedia.org/wiki/Program_optimization#When_to_optimize):
> *[...]about 97% of the time: Premature optimization is the root of all evil*.
That is, don't start to try to optimize your code until you have actually identified a need to do
so. This means your code must actually be working before you start to consider optimization.
Optimization will also often make your code more complex and harder to read. Consider readability
and maintainability and you may find that a small gain in speed is just not worth it.
## Simple timer tests
Python's `timeit` module is very good for testing small things. For example, in order to test if it
is faster to use a `for` loop or a list comprehension you could use the following code:
```python
import timeit
# Time to do 1000000 for loops
timeit.timeit("for i in range(100):\n a.append(i)", setup="a = []")
<<< 10.70982813835144
# Time to do 1000000 list comprehensions
timeit.timeit("a = [i for i in range(100)]")
<<< 5.358283996582031
```
The `setup` keyword is used to set up things that should not be included in the time measurement,
like `a = []` in the first call.
By default the `timeit` function will re-run the given test 1000000 times and returns the *total
time* to do so (so *not* the average per test). A hint is to not use this default for testing
something that includes database writes - for that you may want to use a lower number of repeats
(say 100 or 1000) using the `number=100` keyword.
## Using cProfile
Python comes with its own profiler, named cProfile (this is for cPython, no tests have been done
with `pypy` at this point). Due to the way Evennia's processes are handled, there is no point in
using the normal way to start the profiler (`python -m cProfile evennia.py`). Instead you start the
profiler through the launcher:
evennia --profiler start
This will start Evennia with the Server component running (in daemon mode) under cProfile. You could
instead try `--profile` with the `portal` argument to profile the Portal (you would then need to
[start the Server separately](Start-Stop-Reload)).
Please note that while the profiler is running, your process will use a lot more memory than usual.
Memory usage is even likely to climb over time. So don't leave it running perpetually but monitor it
carefully (for example using the `top` command on Linux or the Task Manager's memory display on
Windows).
Once you have run the server for a while, you need to stop it so the profiler can give its report.
Do *not* kill the program from your task manager or by sending it a kill signal - this will most
likely also mess with the profiler. Instead either use `evennia.py stop` or (which may be even
better), use `@shutdown` from inside the game.
Once the server has fully shut down (this may be a lot slower than usual) you will find that
profiler has created a new file `mygame/server/logs/server.prof`.
## Analyzing the profile
The `server.prof` file is a binary file. There are many ways to analyze and display its contents,
all of which has only been tested in Linux (If you are a Windows/Mac user, let us know what works).
We recommend the
[Runsnake](http://www.vrplumber.com/programming/runsnakerun/) visualizer to see the processor usage
of different processes in a graphical form. For more detailed listing of usage time, you can use
[KCachegrind](http://kcachegrind.sourceforge.net/html/Home.html). To make KCachegrind work with
Python profiles you also need the wrapper script
[pyprof2calltree](https://pypi.python.org/pypi/pyprof2calltree/). You can get pyprof2calltree via
`pip` whereas KCacheGrind is something you need to get via your package manager or their homepage.
How to analyze and interpret profiling data is not a trivial issue and depends on what you are
profiling for. Evennia being an asynchronous server can also confuse profiling. Ask on the mailing
list if you need help and be ready to be able to supply your `server.prof` file for comparison,
along with the exact conditions under which it was obtained.
## The Dummyrunner
It is difficult to test "actual" game performance without having players in your game. For this
reason Evennia comes with the *Dummyrunner* system. The Dummyrunner is a stress-testing system: a
separate program that logs into your game with simulated players (aka "bots" or "dummies"). Once
connected these dummies will semi-randomly perform various tasks from a list of possible actions.
Use `Ctrl-C` to stop the Dummyrunner.
> Warning: You should not run the Dummyrunner on a production database. It will spawn many objects
and also needs to run with general permissions.
To launch the Dummyrunner, first start your server normally (with or without profiling, as above).
Then start a new terminal/console window and active your virtualenv there too. In the new terminal,
try to connect 10 dummy players:
evennia --dummyrunner 10
The first time you do this you will most likely get a warning from Dummyrunner. It will tell you to
copy an import string to the end of your settings file. Quit the Dummyrunner (`Ctrl-C`) and follow
the instructions. Restart Evennia and try `evennia --dummyrunner 10` again. Make sure to remove that
extra settings line when running a public server.
The actions perform by the dummies is controlled by a settings file. The default Dummyrunner
settings file is `evennia/server/server/profiling/dummyrunner_settings.py` but you shouldn't modify
this directly. Rather create/copy the default file to `mygame/server/conf/` and modify it there. To
make sure to use your file over the default, add the following line to your settings file:
```python
DUMMYRUNNER_SETTINGS_MODULE = "server/conf/dummyrunner_settings.py"
```
> Hint: Don't start with too many dummies. The Dummyrunner defaults to taxing the server much more
intensely than an equal number of human players. A good dummy number to start with is 10-100.
Once you have the dummyrunner running, stop it with `Ctrl-C`.
Generally, the dummyrunner system makes for a decent test of general performance; but it is of
course hard to actually mimic human user behavior. For this, actual real-game testing is required.

View file

@ -0,0 +1,120 @@
# Quirks
This is a list of various quirks or common stumbling blocks that people often ask about or report
when using (or trying to use) Evennia. They are not bugs.
### Forgetting to use @reload to see changes to your typeclasses
Firstly: Reloading the server is a safe and usually quick operation which will *not* disconnect any
accounts.
New users tend to forget this step. When editing source code (such as when tweaking typeclasses and
commands or adding new commands to command sets) you need to either use the in-game `@reload`
command or, from the command line do `python evennia.py reload` before you see your changes.
### Web admin to create new Account
If you use the default login system and are trying to use the Web admin to create a new Player
account, you need to consider which `MULTIACCOUNT_MODE` you are in. If you are in
`MULTIACCOUNT_MODE` `0` or `1`, the login system expects each Account to also have a Character
object named the same as the Account - there is no character creation screen by default. If using
the normal mud login screen, a Character with the same name is automatically created and connected
to your Account. From the web interface you must do this manually.
So, when creating the Account, make sure to also create the Character *from the same form* as you
create the Account from. This should set everything up for you. Otherwise you need to manually set
the "account" property on the Character and the "character" property on the Account to point to each
other. You must also set the lockstring of the Character to allow the Account to "puppet" this
particular character.
### Mutable attributes and their connection to the database
When storing a mutable object (usually a list or a dictionary) in an Attribute
```python
object.db.mylist = [1,2,3]
```
you should know that the connection to the database is retained also if you later extract that
Attribute into another variable (what is stored and retrieved is actually a `PackedList` or a
`PackedDict` that works just like their namesakes except they save themselves to the database when
changed). So if you do
```python
alist = object.db.mylist
alist.append(4)
```
this updates the database behind the scenes, so both `alist` and `object.db.mylist` are now
`[1,2,3,4]`
If you don't want this, Evennia provides a way to stably disconnect the mutable from the database by
use of `evennia.utils.dbserialize.deserialize`:
```python
from evennia.utils.dbserialize import deserialize
blist = deserialize(object.db.mylist)
blist.append(4)
```
The property `blist` is now `[1,2,3,4]` whereas `object.db.mylist` remains unchanged. If you want to
update the database you'd need to explicitly re-assign the updated data to the `mylist` Attribute.
### Commands are matched by name *or* alias
When merging [command sets](Commands) it's important to remember that command objects are identified
*both* by key *or* alias. So if you have a command with a key `look` and an alias `ls`, introducing
another command with a key `ls` will be assumed by the system to be *identical* to the first one.
This usually means merging cmdsets will overload one of them depending on priority. Whereas this is
logical once you know how command objects are handled, it may be confusing if you are just looking
at the command strings thinking they are parsed as-is.
### Objects turning to `DefaultObject`
A common confusing error for new developers is finding that one or more objects in-game are suddenly
of the type `DefaultObject` rather than the typeclass you wanted it to be. This happens when you
introduce a critical Syntax error to the module holding your custom class. Since such a module is
not valid Python, Evennia can't load it at all to get to the typeclasses within. To keep on running,
Evennia will solve this by printing the full traceback to the terminal/console and temporarily fall
back to the safe `DefaultObject` until you fix the problem and reload. Most errors of this kind will
be caught by any good text editors. Keep an eye on the terminal/console during a reload to catch
such errors - you may have to scroll up if your window is small.
### Overriding of magic methods
Python implements a system of [magic
methods](https://docs.python.org/3/reference/datamodel.html#emulating-container-types), usually
prefixed and suffixed by double-underscores (`__example__`) that allow object instances to have
certain operations performed on them without needing to do things like turn them into strings or
numbers first-- for example, is `obj1` greater than or equal to `obj2`?
Neither object is a number, but given `obj1.size == "small"` and `obj2.size == "large"`, how might
one compare these two arbitrary English adjective strings to figure out which is greater than the
other? By defining the `__ge__` (greater than or equal to) magic method on the object class in which
you figure out which word has greater significance, perhaps through use of a mapping table
(`{'small':0, 'large':10}`) or other lookup and comparing the numeric values of each.
Evennia extensively makes use of magic methods on typeclasses to do things like initialize objects,
check object existence or iterate over objects in an inventory or container. If you override or
interfere with the return values from the methods Evennia expects to be both present and working, it
can result in very inconsistent and hard-to-diagnose errors.
The moral of the story-- it can be dangerous to tinker with magic methods on typeclassed objects.
Try to avoid doing so.
### Known upstream bugs
- There is currently (Autumn 2017) a bug in the `zope.interface` installer on some Linux Ubuntu
distributions (notably Ubuntu 16.04 LTS). Zope is a dependency of Twisted. The error manifests in
the server not starting with an error that `zope.interface` is not found even though `pip list`
shows it's installed. The reason is a missing empty `__init__.py` file at the root of the zope
package. If the virtualenv is named "evenv" as suggested in the [Getting Started](Getting-Started)
instructions, use the following command to fix it:
```shell
touch evenv/local/lib/python2.7/site-packages/zope/__init__.py
```
This will create the missing file and things should henceforth work correctly.

View file

@ -0,0 +1,116 @@
# Setting up PyCharm
# Directions for setting up PyCharm with Evennia
[PyCharm](https://www.jetbrains.com/pycharm/) is a Python developer's IDE from Jetbrains available
for Windows, Mac and Linux. It is a commercial product but offer free trials, a scaled-down
community edition and also generous licenses for OSS projects like Evennia.
> This page was originally tested on Windows (so use Windows-style path examples), but should work
the same for all platforms.
First, install Evennia on your local machine with [[Getting Started]]. If you're new to PyCharm,
loading your project is as easy as selecting the `Open` option when PyCharm starts, and browsing to
your game folder (the one created with `evennia --init`). We refer to it as `mygame` here.
If you want to be able to examine evennia's core code or the scripts inside your virtualenv, you'll
need to add them to your project too:
1. Go to `File > Open...`
1. Select the folder (i.e. the `evennia` root)
1. Select "Open in current window" and "Add to currently opened projects"
## Setting up the project interpreter
It's a good idea to do this before attempting anything further. The rest of this page assumes your
project is already configured in PyCharm.
1. Go to `File > Settings... > Project: \<mygame\> > Project Interpreter`
1. Click the Gear symbol `> Add local`
1. Navigate to your `evenv/scripts directory`, and select Python.exe
Enjoy seeing all your imports checked properly, setting breakpoints, and live variable watching!
## Attaching PyCharm debugger to Evennia
1. Launch Evennia in your preferred way (usually from a console/terminal)
1. Open your project in PyCharm
1. In the PyCharm menu, select `Run > Attach to Local Process...`
1. From the list, pick the `twistd` process with the `server.py` parameter (Example: `twistd.exe
--nodaemon --logfile=\<mygame\>\server\logs\server.log --python=\<evennia
repo\>\evennia\server\server.py`)
Of course you can attach to the `portal` process as well. If you want to debug the Evennia launcher
or runner for some reason (or just learn how they work!), see Run Configuration below.
> NOTE: Whenever you reload Evennia, the old Server process will die and a new one start. So when
you restart you have to detach from the old and then reattach to the new process that was created.
> To make the process less tedious you can apply a filter in settings to show only the server.py
process in the list. To do that navigate to: `Settings/Preferences | Build, Execution, Deployment |
Python Debugger` and then in `Attach to process` field put in: `twistd.exe" --nodaemon`. This is an
example for windows, I don't have a working mac/linux box.
![Example process filter configuration](https://i.imgur.com/vkSheR8.png)
## Setting up an Evennia run configuration
This configuration allows you to launch Evennia from inside PyCharm. Besides convenience, it also
allows suspending and debugging the evennia_launcher or evennia_runner at points earlier than you
could by running them externally and attaching. In fact by the time the server and/or portal are
running the launcher will have exited already.
1. Go to `Run > Edit Configutations...`
1. Click the plus-symbol to add a new configuration and choose Python
1. Add the script: `\<yourrepo\>\evenv\Scripts\evennia_launcher.py` (substitute your virtualenv if
it's not named `evenv`)
1. Set script parameters to: `start -l` (-l enables console logging)
1. Ensure the chosen interpreter is from your virtualenv
1. Set Working directory to your `mygame` folder (not evenv nor evennia)
1. You can refer to the PyCharm documentation for general info, but you'll want to set at least a
config name (like "MyMUD start" or similar).
Now set up a "stop" configuration by following the same steps as above, but set your Script
parameters to: stop (and name the configuration appropriately).
A dropdown box holding your new configurations should appear next to your PyCharm run button.
Select MyMUD start and press the debug icon to begin debugging. Depending on how far you let the
program run, you may need to run your "MyMUD stop" config to actually stop the server, before you'll
be able start it again.
## Alternative run configuration - utilizing logfiles as source of data
This configuration takes a bit different approach as instead of focusing on getting the data back
through logfiles. Reason for that is this way you can easily separate data streams, for example you
rarely want to follow both server and portal at the same time, and this will allow it. This will
also make sure to stop the evennia before starting it, essentially working as reload command (it
will also include instructions how to disable that part of functionality). We will start by defining
a configuration that will stop evennia. This assumes that `upfire` is your pycharm project name, and
also the game name, hence the `upfire/upfire` path.
1. Go to `Run > Edit Configutations...`\
1. Click the plus-symbol to add a new configuration and choose the python interpreter to use (should
be project default)
1. Name the configuration as "stop evennia" and fill rest of the fields accordingly to the image:
![Stop run configuration](https://i.imgur.com/gbkXhlG.png)
1. Press `Apply`
Now we will define the start/reload command that will make sure that evennia is not running already,
and then start the server in one go.
1. Go to `Run > Edit Configutations...`\
1. Click the plus-symbol to add a new configuration and choose the python interpreter to use (should
be project default)
1. Name the configuration as "start evennia" and fill rest of the fields accordingly to the image:
![Start run configuration](https://i.imgur.com/5YEjeHq.png)
1. Navigate to the `Logs` tab and add the log files you would like to follow. The picture shows
adding `portal.log` which will show itself in `portal` tab when running:
![Configuring logs following](https://i.imgur.com/gWYuOWl.png)
1. Skip the following steps if you don't want the launcher to stop evennia before starting.
1. Head back to `Configuration` tab and press the `+` sign at the bottom, under `Before launch....`
and select `Run another configuration` from the submenu that will pop up.
1. Click `stop evennia` and make sure that it's added to the list like on the image above.
1. Click `Apply` and close the run configuration window.
You are now ready to go, and if you will fire up `start evennia` configuration you should see
following in the bottom panel:
![Example of running alternative configuration](https://i.imgur.com/nTfpC04.png)
and you can click through the tabs to check appropriate logs, or even the console output as it is
still running in interactive mode.

View file

@ -0,0 +1,398 @@
# Unit Testing
*Unit testing* means testing components of a program in isolation from each other to make sure every
part works on its own before using it with others. Extensive testing helps avoid new updates causing
unexpected side effects as well as alleviates general code rot (a more comprehensive wikipedia
article on unit testing can be found [here](http://en.wikipedia.org/wiki/Unit_test)).
A typical unit test set calls some function or method with a given input, looks at the result and
makes sure that this result looks as expected. Rather than having lots of stand-alone test programs,
Evennia makes use of a central *test runner*. This is a program that gathers all available tests all
over the Evennia source code (called *test suites*) and runs them all in one go. Errors and
tracebacks are reported.
By default Evennia only tests itself. But you can also add your own tests to your game code and have
Evennia run those for you.
## Running the Evennia test suite
To run the full Evennia test suite, go to your game folder and issue the command
evennia test evennia
This will run all the evennia tests using the default settings. You could also run only a subset of
all tests by specifying a subpackage of the library:
evennia test evennia.commands.default
A temporary database will be instantiated to manage the tests. If everything works out you will see
how many tests were run and how long it took. If something went wrong you will get error messages.
If you contribute to Evennia, this is a useful sanity check to see you haven't introduced an
unexpected bug.
## Running tests with custom settings file
If you have implemented your own tests for your game (see below) you can run them from your game dir
with
evennia test .
The period (`.`) means to run all tests found in the current directory and all subdirectories. You
could also specify, say, `typeclasses` or `world` if you wanted to just run tests in those subdirs.
Those tests will all be run using the default settings. To run the tests with your own settings file
you must use the `--settings` option:
evennia test --settings settings.py .
The `--settings` option of Evennia takes a file name in the `mygame/server/conf` folder. It is
normally used to swap settings files for testing and development. In combination with `test`, it
forces Evennia to use this settings file over the default one.
## Writing new tests
Evennia's test suite makes use of Django unit test system, which in turn relies on Python's
*unittest* module.
> If you want to help out writing unittests for Evennia, take a look at Evennia's [coveralls.io
page](https://coveralls.io/github/evennia/evennia). There you see which modules have any form of
test coverage and which does not.
To make the test runner find the tests, they must be put in a module named `test*.py` (so `test.py`,
`tests.py` etc). Such a test module will be found wherever it is in the package. It can be a good
idea to look at some of Evennia's `tests.py` modules to see how they look.
Inside a testing file, a `unittest.TestCase` class is used to test a single aspect or component in
various ways. Each test case contains one or more *test methods* - these define the actual tests to
run. You can name the test methods anything you want as long as the name starts with "`test_`".
Your `TestCase` class can also have a method `setUp()`. This is run before each test, setting up and
storing whatever preparations the test methods need. Conversely, a `tearDown()` method can
optionally do cleanup after each test.
To test the results, you use special methods of the `TestCase` class. Many of those start with
"`assert`", such as `assertEqual` or `assertTrue`.
Example of a `TestCase` class:
```python
import unittest
# the function we want to test
from mypath import myfunc
class TestObj(unittest.TestCase):
"This tests a function myfunc."
def test_return_value(self):
"test method. Makes sure return value is as expected."
expected_return = "This is me being nice."
actual_return = myfunc()
# test
self.assertEqual(expected_return, actual_return)
def test_alternative_call(self):
"test method. Calls with a keyword argument."
expected_return = "This is me being baaaad."
actual_return = myfunc(bad=True)
# test
self.assertEqual(expected_return, actual_return)
```
You might also want to read the [documentation for the unittest
module](http://docs.python.org/library/unittest.html).
### Using the EvenniaTest class
Evennia offers a custom TestCase, the `evennia.utils.test_resources.EvenniaTest` class. This class
initiates a range of useful properties on themselves for testing Evennia systems. Examples are
`.account` and `.session` representing a mock connected Account and its Session and `.char1` and
`char2` representing Characters complete with a location in the test database. These are all useful
when testing Evennia system requiring any of the default Evennia typeclasses as inputs. See the full
definition of the `EvenniaTest` class in
[evennia/utils/test_resources.py](https://github.com/evennia/evennia/blob/master/evennia/utils/test_resources.py).
```python
# in a test module
from evennia.utils.test_resources import EvenniaTest
class TestObject(EvenniaTest):
def test_object_search(self):
# char1 and char2 are both created in room1
self.assertEqual(self.char1.search(self.char2.key), self.char2)
self.assertEqual(self.char1.search(self.char1.location.key), self.char1.location)
# ...
```
### Testing in-game Commands
In-game Commands are a special case. Tests for the default commands are put in
`evennia/commands/default/tests.py`. This uses a custom `CommandTest` class that inherits from
`evennia.utils.test_resources.EvenniaTest` described above. `CommandTest` supplies extra convenience
functions for executing commands and check that their return values (calls of `msg()` returns
expected values. It uses Characters and Sessions generated on the `EvenniaTest` class to call each
class).
Each command tested should have its own `TestCase` class. Inherit this class from the `CommandTest`
class in the same module to get access to the command-specific utilities mentioned.
```python
from evennia.commands.default.tests import CommandTest
from evennia.commands.default import general
class TestSet(CommandTest):
"tests the look command by simple call, using Char2 as a target"
def test_mycmd_char(self):
self.call(general.CmdLook(), "Char2", "Char2(#7)")
"tests the look command by simple call, with target as room"
def test_mycmd_room(self):
self.call(general.CmdLook(), "Room",
"Room(#1)\nroom_desc\nExits: out(#3)\n"
"You see: Obj(#4), Obj2(#5), Char2(#7)")
```
### Unit testing contribs with custom models
A special case is if you were to create a contribution to go to the `evennia/contrib` folder that
uses its [own database models](New-Models). The problem with this is that Evennia (and Django) will
only recognize models in `settings.INSTALLED_APPS`. If a user wants to use your contrib, they will
be required to add your models to their settings file. But since contribs are optional you cannot
add the model to Evennia's central `settings_default.py` file - this would always create your
optional models regardless of if the user wants them. But at the same time a contribution is a part
of the Evennia distribution and its unit tests should be run with all other Evennia tests using
`evennia test evennia`.
The way to do this is to only temporarily add your models to the `INSTALLED_APPS` directory when the
test runs. here is an example of how to do it.
> Note that this solution, derived from this [stackexchange
answer](http://stackoverflow.com/questions/502916/django-how-to-create-a-model-dynamically-just-for-
testing#503435) is currently untested! Please report your findings.
```python
# a file contrib/mycontrib/tests.py
from django.conf import settings
import django
from evennia.utils.test_resources import EvenniaTest
OLD_DEFAULT_SETTINGS = settings.INSTALLED_APPS
DEFAULT_SETTINGS = dict(
INSTALLED_APPS=(
'contrib.mycontrib.tests',
),
DATABASES={
"default": {
"ENGINE": "django.db.backends.sqlite3"
}
},
SILENCED_SYSTEM_CHECKS=["1_7.W001"],
)
class TestMyModel(EvenniaTest):
def setUp(self):
if not settings.configured:
settings.configure(**DEFAULT_SETTINGS)
django.setup()
from django.core.management import call_command
from django.db.models import loading
loading.cache.loaded = False
call_command('syncdb', verbosity=0)
def tearDown(self):
settings.configure(**OLD_DEFAULT_SETTINGS)
django.setup()
from django.core.management import call_command
from django.db.models import loading
loading.cache.loaded = False
call_command('syncdb', verbosity=0)
# test cases below ...
def test_case(self):
# test case here
```
### A note on adding new tests
Having an extensive tests suite is very important for avoiding code degradation as Evennia is
developed. Only a small fraction of the Evennia codebase is covered by test suites at this point.
Writing new tests is not hard, it's more a matter of finding the time to do so. So adding new tests
is really an area where everyone can contribute, also with only limited Python skills.
### A note on making the test runner faster
If you have custom models with a large number of migrations, creating the test database can take a
very long time. If you don't require migrations to run for your tests, you can disable them with the
django-test-without-migrations package. To install it, simply:
```
$ pip install django-test-without-migrations
```
Then add it to your `INSTALLED_APPS` in your `server.conf.settings.py`:
```python
INSTALLED_APPS = (
# ...
'test_without_migrations',
)
```
After doing so, you can then run tests without migrations by adding the `--nomigrations` argument:
```
evennia test --settings settings.py --nomigrations .
```
## Testing for Game development (mini-tutorial)
Unit testing can be of paramount importance to game developers. When starting with a new game, it is
recommended to look into unit testing as soon as possible; an already huge game is much harder to
write tests for. The benefits of testing a game aren't different from the ones regarding library
testing. For example it is easy to introduce bugs that affect previously working code. Testing is
there to ensure your project behaves the way it should and continue to do so.
If you have never used unit testing (with Python or another language), you might want to check the
[official Python documentation about unit testing](https://docs.python.org/2/library/unittest.html),
particularly the first section dedicated to a basic example.
### Basic testing using Evennia
Evennia's test runner can be used to launch tests in your game directory (let's call it 'mygame').
Evennia's test runner does a few useful things beyond the normal Python unittest module:
* It creates and sets up an empty database, with some useful objects (accounts, characters and
rooms, among others).
* It provides simple ways to test commands, which can be somewhat tricky at times, if not tested
properly.
Therefore, you should use the command-line to execute the test runner, while specifying your own
game directories (not the one containing evennia). Go to your game directory (referred as 'mygame'
in this section) and execute the test runner:
evennia --settings settings.py test commands
This command will execute Evennia's test runner using your own settings file. It will set up a dummy
database of your choice and look into the 'commands' package defined in your game directory
(`mygame/commands` in this example) to find tests. The test module's name should begin with 'test'
and contain one or more `TestCase`. A full example can be found below.
### A simple example
In your game directory, go to `commands` and create a new file `tests.py` inside (it could be named
anything starting with `test`). We will start by making a test that has nothing to do with Commands,
just to show how unit testing works:
```python
# mygame/commands/tests.py
import unittest
class TestString(unittest.TestCase):
"""Unittest for strings (just a basic example)."""
def test_upper(self):
"""Test the upper() str method."""
self.assertEqual('foo'.upper(), 'FOO')
```
This example, inspired from the Python documentation, is used to test the 'upper()' method of the
'str' class. Not very useful, but it should give you a basic idea of how tests are used.
Let's execute that test to see if it works.
> evennia --settings settings.py test commands
TESTING: Using specified settings file 'server.conf.settings'.
(Obs: Evennia's full test suite may not pass if the settings are very
different from the default. Use 'test .' as arguments to run only tests
on the game dir.)
Creating test database for alias 'default'...
.
----------------------------------------------------------------------
Ran 1 test in 0.001s
OK
Destroying test database for alias 'default'...
We specified the `commands` package to the evennia test command since that's where we put our test
file. In this case we could just as well just said `.` to search all of `mygame` for testing files.
If we have a lot of tests it may be useful to test only a single set at a time though. We get an
information text telling us we are using our custom settings file (instead of Evennia's default
file) and then the test runs. The test passes! Change the "FOO" string to something else in the test
to see how it looks when it fails.
### Testing commands
This section will test the proper execution of the 'abilities' command, as described in the [First
Steps Coding](First-Steps-Coding) page. Follow this tutorial to create the 'abilities' command, we
will need it to test it.
Testing commands in Evennia is a bit more complex than the simple testing example we have seen.
Luckily, Evennia supplies a special test class to do just that ... we just need to inherit from it
and use it properly. This class is called 'CommandTest' and is defined in the
'evennia.commands.default.tests' package. To create a test for our 'abilities' command, we just
need to create a class that inherits from 'CommandTest' and add methods.
We could create a new test file for this but for now we just append to the `tests.py` file we
already have in `commands` from before.
```python
# bottom of mygame/commands/tests.py
from evennia.commands.default.tests import CommandTest
from commands.command import CmdAbilities
from typeclasses.characters import Character
class TestAbilities(CommandTest):
character_typeclass = Character
def test_simple(self):
self.call(CmdAbilities(), "", "STR: 5, AGI: 4, MAG: 2")
```
* Line 1-4: we do some importing. 'CommandTest' is going to be our base class for our test, so we
need it. We also import our command ('CmdAbilities' in this case). Finally we import the
'Character' typeclass. We need it, since 'CommandTest' doesn't use 'Character', but
'DefaultCharacter', which means the character calling the command won't have the abilities we have
written in the 'Character' typeclass.
* Line 6-8: that's the body of our test. Here, a single command is tested in an entire class.
Default commands are usually grouped by category in a single class. There is no rule, as long as
you know where you put your tests. Note that we set the 'character_typeclass' class attribute to
Character. As explained above, if you didn't do that, the system would create a 'DefaultCharacter'
object, not a 'Character'. You can try to remove line 4 and 8 to see what happens when running the
test.
* Line 10-11: our unique testing method. Note its name: it should begin by 'test_'. Apart from
that, the method is quite simple: it's an instance method (so it takes the 'self' argument) but no
other arguments are needed. Line 11 uses the 'call' method, which is defined in 'CommandTest'.
It's a useful method that compares a command against an expected result. It would be like comparing
two strings with 'assertEqual', but the 'call' method does more things, including testing the
command in a realistic way (calling its hooks in the right order, so you don't have to worry about
that).
Line 11 can be understood as: test the 'abilities' command (first parameter), with no argument
(second parameter), and check that the character using it receives his/her abilities (third
parameter).
Let's run our new test:
> evennia --settings settings.py test commands
[...]
Creating test database for alias 'default'...
..
----------------------------------------------------------------------
Ran 2 tests in 0.156s
OK
Destroying test database for alias 'default'...
Two tests were executed, since we have kept 'TestString' from last time. In case of failure, you
will get much more information to help you fix the bug.

View file

@ -0,0 +1,133 @@
# Updating Your Game
Fortunately, it's extremely easy to keep your Evennia server up-to-date. If you haven't already, see
the [Getting Started guide](Getting-Started) and get everything running.
### Updating with the latest Evennia code changes
Very commonly we make changes to the Evennia code to improve things. There are many ways to get told
when to update: You can subscribe to the RSS feed or manually check up on the feeds from
http://www.evennia.com. You can also simply fetch the latest regularly.
When you're wanting to apply updates, simply `cd` to your cloned `evennia` root directory and type:
git pull
assuming you've got the command line client. If you're using a graphical client, you will probably
want to navigate to the `evennia` directory and either right click and find your client's pull
function, or use one of the menus (if applicable).
You can review the latest changes with
git log
or the equivalent in the graphical client. You can also see the latest changes online
[here](https://github.com/evennia/evennia/blob/master/CHANGELOG.md).
You will always need to do `evennia reload` (or `reload` from -in-game) from your game-dir to have
the new code affect your game. If you want to be really sure you should run a full `evennia reboot`
so that both Server and Portal can restart (this will disconnect everyone though, so if you know the
Portal has had no updates you don't have to do that).
### Upgrading Evennia dependencies
On occasion we update the versions of third-party libraries Evennia depend on (or we may add a new
dependency). This will be announced on the mailing list/forum. If you run into errors when starting
Evennia, always make sure you have the latest versions of everything. In some cases, like for
Django, starting the server may also give warning saying that you are using a working, but too-old
version that should not be used in production.
Upgrading `evennia` will automatically fetch all the latest packages that it now need. First `cd` to
your cloned `evennia` folder. Make sure your `virtualenv` is active and use
pip install --upgrade -e .
Remember the period (`.`) at the end - that applies the upgrade to the current location (your
`evennia` dir).
> The `-e` means that we are _linking_ the evennia sources rather than copying them into the
environment. This means we can most of the time just update the sources (with `git pull`) and see
those changes directly applied to our installed `evennia` package. Without installing/upgrading the
package with `-e`, we would have to remember to upgrade the package every time we downloaded any new
source-code changes.
Follow the upgrade output to make sure it finishes without errors. To check what packages are
currently available in your python environment after the upgrade, use
pip list
This will show you the version of all installed packages. The `evennia` package will also show the
location of its source code.
## Migrating the Database Schema
Whenever we change the database layout of Evennia upstream (such as when we add new features) you
will need to *migrate* your existing database. When this happens it will be clearly noted in the
`git log` (it will say something to the effect of "Run migrations"). Database changes will also be
announced on the Evennia [mailing list](https://groups.google.com/forum/#!forum/evennia).
When the database schema changes, you just go to your game folder and run
evennia migrate
> Hint: If the `evennia` command is not found, you most likely need to activate your
[virtualenv](Glossary#virtualenv).
## Resetting your database
Should you ever want to start over completely from scratch, there is no need to re-download Evennia
or anything like that. You just need to clear your database. Once you are done, you just rebuild it
from scratch as described in [step 2](Getting-Started#step-2-setting-up-your-game) of the [Getting
Started guide](Getting-Started).
First stop a running server with
evennia stop
If you run the default `SQlite3` database (to change this you need to edit your `settings.py` file),
the database is actually just a normal file in `mygame/server/` called `evennia.db3`. *Simply delete
that file* - that's it. Now run `evennia migrate` to recreate a new, fresh one.
If you run some other database system you can instead flush the database:
evennia flush
This will empty the database. However, it will not reset the internal counters of the database, so
you will start with higher dbref values. If this is okay, this is all you need.
Django also offers an easy way to start the database's own management should we want more direct
control:
evennia dbshell
In e.g. MySQL you can then do something like this (assuming your MySQL database is named "Evennia":
mysql> DROP DATABASE Evennia;
mysql> exit
> NOTE: Under Windows OS, in order to access SQLite dbshell you need to [download the SQLite
command-line shell program](https://www.sqlite.org/download.html). It's a single executable file
(sqlite3.exe) that you should place in the root of either your MUD folder or Evennia's (it's the
same, in both cases Django will find it).
## More about schema migrations
If and when an Evennia update modifies the database *schema* (that is, the under-the-hood details as
to how data is stored in the database), you must update your existing database correspondingly to
match the change. If you don't, the updated Evennia will complain that it cannot read the database
properly. Whereas schema changes should become more and more rare as Evennia matures, it may still
happen from time to time.
One way one could handle this is to apply the changes manually to your database using the database's
command line. This often means adding/removing new tables or fields as well as possibly convert
existing data to match what the new Evennia version expects. It should be quite obvious that this
quickly becomes cumbersome and error-prone. If your database doesn't contain anything critical yet
it's probably easiest to simply reset it and start over rather than to bother converting.
Enter *migrations*. Migrations keeps track of changes in the database schema and applies them
automatically for you. Basically, whenever the schema changes we distribute small files called
"migrations" with the source. Those tell the system exactly how to implement the change so you don't
have to do so manually. When a migration has been added we will tell you so on Evennia's mailing
lists and in commit messages -
you then just run `evennia migrate` to be up-to-date again.

View file

@ -0,0 +1,37 @@
# Using Travis
Evennia uses [Travis CI](http://travis-ci.org/) to check that it's building successfully after every
commit to its Github repository (you can for example see the `build: passing` badge at the top of
Evennia's [Readme file](https://github.com/evennia/evennia)). If your game is open source on Github
you may also use Travis for free. See [the Travis docs](http://docs.travis-ci.com/user/getting-
started/) for how to get started.
After logging in you will get to point Travis to your repository on github. One further thing you
need to set up yourself is a Travis config file named `.travis.yml` (note the initial period `.`).
This should be created in the root of your game directory. The idea with this file is that it
describes what Travis needs to import and build in order to create an instance of Evennia from
scratch and then run validation tests on it. Here is an example:
``` yaml
language: python
python:
- "2.7"
install:
- git clone https://github.com/evennia/evennia.git
- cd evennia
- pip install -e .
- cd $TRAVIS_BUILD_DIR
script:
- evennia migrate
- evennia test evennia
- evennia test
```
This will tell travis how to download Evennia, install it, set up a database and then run the test
suite.
You need to add this file to git (`git add .travis.yml`) and then commit your changes before Travis
will be able to see it.
For properly testing your game you of course also need to write unittests. [We have a page](Unit-
Testing) on how we set those up for Evennia, you should be able to refer to that for making tests
fitting your game.

View file

@ -0,0 +1,475 @@
# Version Control
Version control software allows you to track the changes you make to your code, as well as being
able to easily backtrack these changes, share your development efforts and more. Even if you are not
contributing to Evennia itself, and only wish to develop your own MU* using Evennia, having a
version control system in place is a good idea (and standard coding practice). For an introduction
to the concept, start with the Wikipedia article
[here](http://en.wikipedia.org/wiki/Version_control). Evennia uses the version control system
[Git](https://git-scm.com/) and this is what will be covered henceforth. Note that this page also
deals with commands for Linux operating systems, and the steps below may vary for other systems,
however where possible links will be provided for alternative instructions.
For more help on using Git, please refer to the [Official GitHub
documentation](https://help.github.com/articles/set-up-git#platform-all).
## Setting up Git
If you have gotten Evennia installed, you will have Git already and can skip to **Step 2** below.
Otherwise you will need to install Git on your platform. You can find expanded instructions for
installation [here](http://git-scm.com/book/en/Getting-Started-Installing-Git).
### Step 1: Install Git
- **Fedora Linux**
yum install git-core
- **Debian Linux** _(Ubuntu, Linux Mint, etc.)_
apt-get install git
- **Windows**: It is recommended to use [Git for Windows](http://msysgit.github.io/).
- **Mac**: Mac platforms offer two methods for installation, one via MacPorts, which you can find
out about [here](http://git-scm.com/book/en/Getting-Started-Installing-Git#Installing-on-Mac), or
you can use the [Git OSX Installer](https://sourceforge.net/projects/git-osx-installer/).
### Step 2: Define user/e-mail Settings for Git
To avoid a common issue later, you will need to set a couple of settings; first you will need to
tell Git your username, followed by your e-mail address, so that when you commit code later you will
be properly credited.
> Note that your commit information will be visible to everyone if you ever contribute to Evennia or
use an online service like github to host your code. So if you are not comfortable with using your
real, full name online, put a nickname here.
1. Set the default name for git to use when you commit:
git config --global user.name "Your Name Here"
2. Set the default email for git to use when you commit:
git config --global user.email "your_email@example.com"
## Putting your game folder under version control
> Note: The game folder's version control is completely separate from Evennia's repository.
After you have set up your game you will have created a new folder to host your particular game
(let's call this folder `mygame` for now).
This folder is *not* under version control at this point.
git init mygame
Your mygame folder is now ready for version control! Now add all the content and make a first
commit:
cd mygame
git add *
git commit -m "Initial commit"
Read on for help on what these commands do.
### Tracking files
When working on your code or fix bugs in your local branches you may end up creating new files. If
you do you must tell Git to track them by using the add command:
```
git add <filename>
```
You can check the current status of version control with `git status`. This will show if you have
any modified, added or otherwise changed files. Some files, like database files, logs and temporary
PID files are usually *not* tracked in version control. These should either not show up or have a
question mark in front of them.
### Controlling tracking
You will notice that some files are not covered by your git version control, notably your settings
file (`mygame/server/conf/settings.py`) and your sqlite3 database file `mygame/server/evennia.db3`.
This is controlled by the hidden file `mygame/.gitignore`. Evennia creates this file as part of the
creation of your game directory. Everything matched in this file will be ignored by GIT. If you want
to, for example, include your settings file for collaborators to access, remove that entry in
`.gitignore`.
> Note: You should *never* put your sqlite3 database file into git by removing its entry in
`.gitignore`. GIT is for backing up your code, not your database. That way lies madness and a good
chance you'll confuse yourself so that after a few commits and reverts don't know what is in your
database or not. If you want to backup your database, do so by simply copying the file on your hard
drive to a backup-name.
### Committing your Code
> Committing means storing the current snapshot of your code within git. This creates a "save point"
or "history" of your development process. You can later jump back and forth in your history, for
example to figure out just when a bug was introduced or see what results the code used to produce
compared to now.
It's usually a good idea to commit your changes often. Committing is fast and local only - you will
never commit anything online at this point. To commit your changes, use
```
git commit --all
```
This will save all changes you made since last commit. The command will open a text editor where you
can add a message detailing the changes you've made. Make it brief but informative. You can see the
history of commits with `git log`. If you don't want to use the editor you can set the message
directly by using the `-m` flag:
```
git commit --all -m "This fixes a bug in the combat code."
```
### Changing your mind
If you have non-committed changes that you realize you want to throw away, you can do the following:
```
git checkout <file to revert>
```
This will revert the file to the state it was in at your last `commit`, throwing away the changes
you did to it since. It's a good way to make wild experiments without having to remember just what
you changed. If you do ` git checkout .` you will throw away _all_ changes since the last commit.
### Pushing your code online
So far your code is only located on your private machine. A good idea is to back it up online. The
easiest way to do this is to push it to your own remote repository on GitHub.
1. Make sure you have your game directory setup under git version control as described above. Make
sure to commit any changes.
2. Create a new, empty repository on Github. Github explains how
[here](https://help.github.com/articles/create-a-repo/) (do *not* "Initialize the repository with a
README" or else you'll create unrelated histories).
3. From your local game dir, do `git remote add origin <github URL>` where `<github URL>` is the URL
to your online repo. This tells your game dir that it should be pushing to the remote online dir.
4. `git remote -v` to verify the online dir.
5. `git push origin master` now pushes your game dir online so you can see it on github.com.
You can commit your work locally (`git commit --all -m "Make a change that ..."`) as many times as
you want. When you want to push those changes to your online repo, you do `git push`. You can also
`git clone <url_to_online_repo>` from your online repo to somewhere else (like your production
server) and henceforth do `git pull` to update that to the latest thing you pushed.
Note that GitHub's repos are, by default publicly visible by all. Creating a publicly visible online
clone might not be what you want for all parts of your development process - you may prefer a more
private venue when sharing your revolutionary work with your team. If that's the case you can change
your repository to "Private" in the github settings. Then your code will only be visible to those
you specifically grant access.
## Forking Evennia
This helps you set up an online *fork* of Evennia so you can easily commit fixes and help with
upstream development.
### Step 1: Fork the evennia/master repository
> Before proceeding with the following step, make sure you have registered and created an account on
[GitHub.com](https://github.com/). This is necessary in order to create a fork of Evennia's master
repository, and to push your commits to your fork either for yourself or for contributing to
Evennia.
A _fork_ is a clone of the master repository that you can make your own commits and changes to. At
the top of [this page](https://github.com/evennia/evennia), click the "Fork" button, as it appears
below. ![](https://github-images.s3.amazonaws.com/help/bootcamp/Bootcamp-Fork.png)
### Step 2: Clone your fork
The fork only exists online as of yet. In a terminal, change your directory to the folder you wish
to develop in. From this directory run the following command:
```
git clone https://github.com/yourusername/evennia.git
```
This will download your fork to your computer. It creates a new folder `evennia/` at your current
location.
### Step 3: Configure remotes
A _remote_ is a repository stored on another computer, in this case on GitHub's server. When a
repository is cloned, it has a default remote called `origin`. This points to your fork on GitHub,
not the original repository it was forked from. To easily keep track of the original repository
(that is, Evennia's official repository), you need to add another remote. The standard name for this
remote is "upstream".
Below we change the active directory to the newly cloned "evennia" directory and then assign the
original Evennia repository to a remote called "upstream":
```
cd evennia
git remote add upstream https://github.com/evennia/evennia.git
```
If you also want to access Evennia's `develop` branch (the bleeding edge development branch) do the
following:
```
git fetch upstream develop
git checkout develop
```
You should now have the upstream branch available locally. You can use this instead of `master`
below if you are contributing new features rather than bug fixes.
## Working with your fork
> A _branch_ is a separate instance of your code. Changes you do to code in a branch does not affect
that in other branches (so if you for example add/commit a file to one branch and then switches to
another branch, that file will be gone until you switch back to the first branch again). One can
switch between branches at will and create as many branches as one needs for a given project. The
content of branches can also be merged together or deleted without affecting other branches. This is
not only a common way to organize development but also to test features without messing with
existing code.
The default _branch_ of git is called the "master" branch. As a rule of thumb, you should *never*
make modifications directly to your local copy of the master branch. Rather keep the master clean
and only update it by pulling our latest changes to it. Any work you do should instead happen in a
local, other branches.
### Making a work branch
```
git checkout -b myfixes
```
This command will checkout and automatically create the new branch `myfixes` on your machine. If you
stared out in the master branch, *myfixes* will be a perfect copy of the master branch. You can see
which branch you are on with `git branch` and change between different branches with `git checkout
<branchname>`.
Branches are fast and cheap to create and manage. It is common practice to create a new branch for
every bug you want to work on or feature you want to create, then create a *pull request* for that
branch to be merged upstream (see below). Not only will this organize your work, it will also make
sure that *your* master branch version of Evennia is always exactly in sync with the upstream
version's master branch.
### Updating with upstream changes
When Evennia's official repository updates, first make sure to commit all your changes to your
branch and then checkout the "clean" master branch:
```
git commit --all
git checkout master
```
Pull the latest changes from upstream:
```
git pull upstream master
```
This should sync your local master branch with upstream Evennia's master branch. Now we go back to
our own work-branch (let's say it's still called "myfixes") and _merge_ the updated master into our
branch.
```
git checkout myfixes
git merge master
```
If everything went well, your `myfixes` branch will now have the latest version of Evennia merged
with whatever changes you have done. Use `git log` to see what has changed. You may need to restart
the server or run `manage.py migrate` if the database schema changed (this will be seen in the
commit log and on the mailing list). See the [Git manuals](http://git-scm.com/documentation) for
learning more about useful day-to-day commands, and special situations such as dealing with merge
collisions.
## Sharing your Code Publicly
Up to this point your `myfixes` branch only exists on your local computer. No one else can see it.
If you want a copy of this branch to also appear in your online fork on GitHub, make sure to have
checked out your "myfixes" branch and then run the following:
```
git push -u origin myfixes
```
This will create a new _remote branch_ named "myfixes" in your online repository (which is refered
to as "origin" by default); the `-u` flag makes sure to set this to the default push location.
Henceforth you can just use `git push` from your myfixes branch to push your changes online. This is
a great way to keep your source backed-up and accessible. Remember though that by default your
repository will be public so everyone will be able to browse and download your code (same way as you
can with Evennia itself). If you want secrecy you can change your repository to "Private" in the
Github settings. Note though that if you do, you might have trouble contributing to Evennia (since
we can't see the code you want to share).
*Note: If you hadn't setup a public key on GitHub or aren't asked for a username/password, you might
get an error `403: Forbidden Access` at this stage. In that case, some users have reported that the
workaround is to create a file `.netrc` under your home directory and add your credentials there:*
```bash
machine github.com
login <my_github_username>
password <my_github_password>
```
## Committing fixes to Evennia
_Contributing_ can mean both bug-fixes or adding new features to Evennia. Please note that if your
change is not already listed and accepted in the [Issue
Tracker](https://github.com/evennia/evennia/issues), it is recommended that you first hit the
developer mailing list or IRC chat to see beforehand if your feature is deemed suitable to include
as a core feature in the engine. When it comes to bug-fixes, other developers may also have good
input on how to go about resolving the issue.
To contribute you need to have [forked Evennia](Version-Control#forking-evennia) first. As described
above you should do your modification in a separate local branch (not in the master branch). This
branch is what you then present to us (as a *Pull request*, PR, see below). We can then merge your
change into the upstream master and you then do `git pull` to update master usual. Now that the
master is updated with your fixes, you can safely delete your local work branch. Below we describe
this work flow.
First update the Evennia master branch to the latest Evennia version:
```
git checkout master
git pull upstream master
```
Next, create a new branch to hold your contribution. Let's call it the "fixing_strange_bug" branch:
```
git checkout -b fixing_strange_bug
```
It is wise to make separate branches for every fix or series of fixes you want to contribute. You
are now in your new `fixing_strange_bug` branch. You can list all branches with `git branch` and
jump between branches with `git checkout <branchname>`. Code and test things in here, committing as
you go:
```
git commit --all -m "Fix strange bug in look command. Resolves #123."
```
You can make multiple commits if you want, depending on your work flow and progress. Make sure to
always make clear and descriptive commit messages so it's easy to see what you intended. To refer
to, say, issue number 123, write `#123`, it will turn to a link on GitHub. If you include the text
"Resolves #123", that issue will be auto-closed on GitHub if your commit gets merged into main
Evennia.
>If you refer to in-game commands that start with `@`(such as `@examine`), please put them in
backticks \`, for example \`@examine\`. The reason for this is that GitHub uses `@username` to refer
to GitHub users, so if you forget the ticks, any user happening to be named `examine` will get a
notification ....
If you implement multiple separate features/bug-fixes, split them into different branches if they
are very different and should be handled as separate PRs. You can do any number of commits to your
branch as you work. Once you are at a stage where you want to show the world what you did you might
want to consider making it clean for merging into Evennia's master branch by using [git
rebase](https://www.git-scm.com/book/en/v2/Git-Branching-Rebasing) (this is not always necessary,
and if it sounds too hard, say so and we'll handle it on our end).
Once you are ready, push your work to your online Evennia fork on github, in a new remote branch:
```
git push -u origin fixing_strange_bug
```
The `-u` flag is only needed the first time - this tells GIT to create a remote branch. If you
already created the remote branch earlier, just stand in your `fixing_strange_bug` branch and do
`git push`.
Now you should tell the Evennia developers that they should consider merging your brilliant changes
into Evennia proper. [Create a pull request](https://github.com/evennia/evennia/pulls) and follow
the instructions. Make sure to specifically select your `fixing_strange_bug` branch to be the source
of the merge. Evennia developers will then be able to examine your request and merge it if it's
deemed suitable.
Once your changes have been merged into Evennia your local `fixing_strange_bug` can be deleted
(since your changes are now available in the "clean" Evennia repository). Do
```
git branch -D fixing_strange_bug
```
to delete your work branch. Update your master branch (`checkout master` and then `git pull`) and
you should get your fix back, now as a part of official Evennia!
## GIT tips and tricks
Some of the GIT commands can feel a little long and clunky if you need to do them often. Luckily you
can create aliases for those. Here are some useful commands to run:
```
# git st
# - view brief status info
git config --global alias.st 'status -s'
```
Above, you only need to ever enter the `git config ...` command once - you have then added the new
alias. Afterwards, just do `git st` to get status info. All the examples below follow the same
template.
```
# git cl
# - clone a repository
git config --global alias.cl clone
```
```
# git cma "commit message"
# - commit all changes without opening editor for message
git config --global alias.cma 'commit -a -m'
```
```
# git ca
# - amend text to your latest commit message
git config --global alias.ca 'commit --amend'
```
```
# git fl
# - file log; shows diffs of files in latest commits
git config --global alias.fl 'log -u'
```
```
# git co [branchname]
# - checkout
git config --global alias.co checkout
```
```
# git br <branchname>
# - create branch
git config --global alias.br branch
```
```
# git ls
# - view log tree
git config --global alias.ls 'log --pretty=format:"%C(green)%h\ %C(yellow)[%ad]%Cred%d\
%Creset%s%Cblue\ [%cn]" --decorate --date=relative --graph'
```
```
# git diff
# - show current uncommitted changes
git config --global alias.diff 'diff --word-diff'
```
```
# git grep <query>
# - search (grep) codebase for a search criterion
git config --global alias.grep 'grep -Ii'
```
To get a further feel for GIT there is also [a good YouTube talk about
it](https://www.youtube.com/watch?v=1ffBJ4sVUb4#t=1m58s) - it's a bit long but it will help you
understand the underlying ideas behind GIT
(which in turn makes it a lot more intuitive to use).