Fix unit test. Update/remove more coding docs

This commit is contained in:
Griatch 2022-11-25 21:22:34 +01:00
parent 00035c15d5
commit ff6e01475d
11 changed files with 33 additions and 841 deletions

View file

@ -1,272 +0,0 @@
# Coding FAQ
*This FAQ page is for users to share their solutions to coding problems. Keep it brief and link to
the docs if you can rather than too lengthy explanations. Don't forget to check if an answer already
exists before answering - maybe you can clarify that answer rather than to make a new Q&A section.*
## Removing default commands
**Q:** How does one *remove* (not replace) e.g. the default `get` [Command](../Components/Commands.md) from the Character [Command Set](../Components/Command-Sets.md)?
**A:** Go to `mygame/commands/default_cmdsets.py`. Find the `CharacterCmdSet` class. It has one
method named `at_cmdset_creation`. At the end of that method, add the following line:
`self.remove(default_cmds.CmdGet())`. See the [Adding Commands Tutorial](../Howtos/Beginner-Tutorial/Part1/Beginner-Tutorial-Adding-Commands.md)
for more info.
## Preventing character from moving based on a condition
**Q:** How does one keep a character from using any exit, if they meet a certain condition? (I.E. in
combat, immobilized, etc.)
**A:** The `at_pre_move` hook is called by Evennia just before performing any move. If it returns
`False`, the move is aborted. Let's say we want to check for an [Attribute](../Components/Attributes.md) `cantmove`.
Add the following code to the `Character` class:
```python
def at_pre_move(self, destination):
"Called just before trying to move"
if self.db.cantmove: # replace with condition you want to test
self.msg("Something is preventing you from moving!")
return False
return True
```
## Reference initiating object in an EvMenu command.
**Q:** An object has a Command on it starts up an EvMenu instance. How do I capture a reference to
that object for use in the menu?
**A:** When an [EvMenu](../Components/EvMenu.md) is started, the menu object is stored as `caller.ndb._evmenu`.
This is a good place to store menu-specific things since it will clean itself up when the menu
closes. When initiating the menu, any additional keywords you give will be available for you as
properties on this menu object:
```python
class MyObjectCommand(Command):
# A Command stored on an object (the object is always accessible from
# the Command as self.obj)
def func(self):
# add the object as the stored_obj menu property
EvMenu(caller, ..., stored_obj=self.obj)
```
Inside the menu you can now access the object through `caller.ndb._evmenu.stored_obj`.
## Selectively turn off commands in a room
**Q:** I want certain commands to turn off in a given room. They should still work normally for
staff.
**A:** This is done using a custom cmdset on a room [locked with the 'call' lock type](../Components/Locks.md). Only
if this lock is passed will the commands on the room be made available to an object inside it. Here
is an example of a room where certain commands are disabled for non-staff:
```python
# in mygame/typeclasses/rooms.py
from evennia import default_commands, CmdSet
class CmdBlocking(default_commands.MuxCommand):
# block commands give, get, inventory and drop
key = "give"
aliases = ["get", "inventory", "drop"]
def func(self):
self.caller.msg("You cannot do that in this room.")
class BlockingCmdSet(CmdSet):
key = "blocking_cmdset"
# default commands have prio 0
priority = 1
def at_cmdset_creation(self):
self.add(CmdBlocking())
class BlockingRoom(Room):
def at_object_creation(self):
self.cmdset.add(BlockingCmdSet, persistent=True)
# only share commands with players in the room that
# are NOT Builders or higher
self.locks.add("call:not perm(Builders)")
```
After `reload`, make some `BlockingRooms` (or switch a room to it with `@typeclass`). Entering one
will now replace the given commands for anyone that does not have the `Builders` or higher
permission. Note that the 'call' lock is special in that even the superuser will be affected by it
(otherwise superusers would always see other player's cmdsets and a game would be unplayable for superusers).
## Select Command based on a condition
**Q:** I want a command to be available only based on a condition. For example I want the "werewolf"
command to only be available on a full moon, from midnight to three in-game time.
**A:** This is easiest accomplished by putting the "werewolf" command on the Character as normal, but to [lock](../Components/Locks.md) it with the "cmd" type lock. Only if the "cmd" lock type is passed will the
command be available.
```python
# in mygame/commands/command.py
from evennia import Command
class CmdWerewolf(Command):
key = "werewolf"
# lock full moon, between 00:00 (midnight) and 03:00.
locks = "cmd:is_full_moon(0, 3)"
def func(self):
# ...
```
Add this to the [default cmdset as usual](../Howtos/Beginner-Tutorial/Part1/Beginner-Tutorial-Adding-Commands.md). The `is_full_moon` [lock function](../Components/Locks.md#lock-functions) does not yet exist. We must create that:
```python
# in mygame/server/conf/lockfuncs.py
def is_full_moon(accessing_obj, accessed_obj,
starthour, endhour, *args, **kwargs):
# calculate if the moon is full here and
# if current game time is between starthour and endhour
# return True or False
```
After a `reload`, the `werewolf` command will be available only at the right time, that is when the
`is_full_moon` lock function returns True.
## Automatically updating code when reloading
**Q:** I have a development server running Evennia. Can I have the server update its code-base when
I reload?
**A:** Having a development server that pulls updated code whenever you reload it can be really
useful if you have limited shell access to your server, or want to have it done automatically. If
you have your project in a configured Git environment, it's a matter of automatically calling `git pull` when you reload. And that's pretty straightforward:
In `/server/conf/at_server_startstop.py`:
```python
import subprocess
# ... other hooks ...
def at_server_reload_stop():
"""
This is called only time the server stops before a reload.
"""
print("Pulling from the game repository...")
process = subprocess.call(["git", "pull"], shell=False)
```
That's all. We call `subprocess` to execute a shell command (that code works on Windows and Linux, assuming the current directory is your game directory, which is probably the case when you run Evennia). `call` waits for the process to complete, because otherwise, Evennia would reload on partially-modified code, which would be problematic.
Now, when you enter `reload` on your development server, the game repository is updated from the configured remote repository (Github, for instance). Your development cycle could resemble something like:
1. Coding on the local machine.
2. Testing modifications.
3. Committing once, twice or more (being sure the code is still working, unittests are pretty useful here).
4. When the time comes, login to the development server and run `@reload`.
The reloading might take one or two additional seconds, since Evennia will pull from your remote Git repository. But it will reload on it and you will have your modifications ready, without needing
connecting to your server using SSH or something similar.
## Changing all exit messages
**Q:** How can I change the default exit messages to something like "XXX leaves east" or "XXX
arrives from the west"?
**A:** the default exit messages are stored in two hooks, namely `announce_move_from` and `announce_move_to`, on the `Character` typeclass (if what you want to change is the message other characters will see when a character exits).
These two hooks provide some useful features to easily update the message to be displayed. They take both the default message and mapping as argument. You can easily call the parent hook with these information:
* The message represents the string of characters sent to characters in the room when a character leaves.
* The mapping is a dictionary containing additional mappings (you will probably not need it for simple customization).
It is advisable to look in the [code of both hooks](evennia.objects.objects.DefaultCharacter), and read the hooks' documentation. The explanations on how to quickly update the message are shown below:
```python
# In typeclasses/characters.py
"""
Characters
"""
from evennia import DefaultCharacter
class Character(DefaultCharacter):
"""
The default character class.
...
"""
def announce_move_from(self, destination, msg=None, mapping=None):
"""
Called if the move is to be announced. This is
called while we are still standing in the old
location.
Args:
destination (Object): The place we are going to.
msg (str, optional): a replacement message.
mapping (dict, optional): additional mapping objects.
You can override this method and call its parent with a
message to simply change the default message. In the string,
you can use the following as mappings (between braces):
object: the object which is moving.
exit: the exit from which the object is moving (if found).
origin: the location of the object before the move.
destination: the location of the object after moving.
"""
super().announce_move_from(destination, msg="{object} leaves {exit}.")
def announce_move_to(self, source_location, msg=None, mapping=None):
"""
Called after the move if the move was not quiet. At this point
we are standing in the new location.
Args:
source_location (Object): The place we came from
msg (str, optional): the replacement message if location.
mapping (dict, optional): additional mapping objects.
You can override this method and call its parent with a
message to simply change the default message. In the string,
you can use the following as mappings (between braces):
object: the object which is moving.
exit: the exit from which the object is moving (if found).
origin: the location of the object before the move.
destination: the location of the object after moving.
"""
super().announce_move_to(source_location, msg="{object} arrives from the {exit}.")
```
We override both hooks, but call the parent hook to display a different message. If you read the provided docstrings, you will better understand why and how we use mappings (information between braces). You can provide additional mappings as well, if you want to set a verb to move, for instance, or other, extra information.
## Add parsing with the "to" delimiter
**Q:** How do I change commands to undestand say `give obj to target` as well as the default `give obj = target`?
**A:** You can make change the default `MuxCommand` parent with your own class making a small change in its `parse` method:
```python
# in mygame/commands/command.py
from evennia import default_cmds
class MuxCommand(default_cmds.MuxCommand):
def parse(self):
"""Implement an additional parsing of 'to'"""
super().parse()
if " to " in self.args:
self.lhs, self.rhs = self.args.split(" to ", 1)
```
Next you change the parent of the default commands in settings:
```python
COMMAND_DEFAULT_CLASS = "commands.command.MuxCommand"
```
Do a `reload` and all default commands will now use your new tweaked parent class. A copy of the
MuxCommand class is also found commented-out in the `mygame/commands/command.py` file.
## Non-latin characters in EvTable
**Q:** When using e.g. Chinese characters in EvTable, some lines appear to be too wide, for example
```
+------+------+
| | |
| 测试 | 测试 |
| | |
+~~~~~~+~~~~~~+
```
**A:** The reason for this is because certain non-latin characters are *visually* much wider than their len() suggests. There is little Evennia can (reliably) do about this. If you are using such characters, you need to make sure to use a suitable mono-spaced font where are width are equal. You can set this in your web client and need to recommend it for telnet-client users. See [this discussion](https://github.com/evennia/evennia/issues/1522) where some suitable fonts are suggested.

View file

@ -1,20 +1,20 @@
# Coding and development help
This documentation aims to help you set up a sane development environment to
make your game, also if you never coded before. If you are an experienced coder, much of this will be familiar to you, but some things may still be useful.
make your game, also if you never coded before.
See also the [Beginner Tutorial](../Howtos/Beginner-Tutorial/Beginner-Tutorial-Overview.md).
```{toctree}
:maxdepth: 2
Evennia-Code-Style.md
Version-Control.md
Debugging.md
Unit-Testing.md
Profiling.md
Evennia-Code-Style.md
Coding-FAQ.md
Quirks.md
Continuous-Integration.md
Setting-up-PyCharm.md
```
## Evennia Changelog
@ -25,12 +25,3 @@ Quirks.md
Changelog.md
```
## Third-party integrations
```{toctree}
:maxdepth: 1
Continuous-Integration.md
Setting-up-PyCharm.md
```

View file

@ -1,7 +1,5 @@
# Continuous Integration (CI)
One of the advantages of Evennia over traditional MU* development systems is that Evennia can integrate into enterprise-level integration environments and source control.
[Continuous Integration (CI)](https://www.thoughtworks.com/continuous-integration) is a development practice that requires developers to integrate code into a shared repository. Each check-in is then verified by an automated build, allowing teams to detect problems early. This can be set up to safely deploy data to a production server only after tests have passed, for example.
For Evennia, continuous integration allows an automated build process to:
@ -13,7 +11,7 @@ For Evennia, continuous integration allows an automated build process to:
* Publish those files to the server directory
* Reload the game.
## List of CI Evennia tutorials
## Continuous-Integration guides
There are a lot of tools and services providing CI functionality. Here are a few that people have used with Evennia:
@ -25,4 +23,6 @@ Continuous-Integration-TeamCity.md
```
- Evennia is itself making heavy use of [github actions]()
[This is an overview of other tools](https://www.atlassian.com/continuous-delivery/continuous-integration/tools) (external link).

View file

@ -1,143 +0,0 @@
# 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](../Components/Commands.md) 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.
## Things to remember about the flat API
The flat API is a series of 'shortcuts' on the `evennia` main library root (defined in
`evennia/__init__.py`). Its componentas are documented [as part of the auto-documentation](../Evennia-API.md).
### 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

@ -13,10 +13,7 @@ need to add them to your project too:
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.
It's a good idea to set up the interpreter 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`
@ -24,7 +21,7 @@ project is already configured in PyCharm.
Enjoy seeing all your imports checked properly, setting breakpoints, and live variable watching!
## Attaching PyCharm debugger to Evennia
## Debug Evennia from inside PyCharm
1. Launch Evennia in your preferred way (usually from a console/terminal)
1. Open your project in PyCharm
@ -40,7 +37,7 @@ or runner for some reason (or just learn how they work!), see Run Configuration
![Example process filter configuration](https://i.imgur.com/vkSheR8.png)
## Setting up an Evennia run configuration
## Run Evennia from inside PyCharm
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.
@ -56,7 +53,7 @@ Now set up a "stop" configuration by following the same steps as above, but set
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 config - utilizing logfiles as source of data
### Alternative config - 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.

View file

@ -229,12 +229,9 @@ optional models regardless of if the user wants them. But at the same time a con
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.
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.
> 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
@ -286,8 +283,7 @@ class TestMyModel(BaseEvenniaTest):
## 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
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:
```

View file

@ -1,382 +0,0 @@
# Glossary
This explains common recurring terms used in the Evennia docs. It will be expanded as needed.
- _[account](./Glossary.md#account)_ - the player's account on the game
- _[admin-site](./Glossary.md#admin-site)_ - the Django web page for manipulating the database
- _[attribute](./Glossary.md#attribute)_ - persistent, custom data stored on typeclasses
- _[channel](./Glossary.md#channel)_ - game communication channels
- _[character](./Glossary.md#character)_ - the player's avatar in the game, controlled from
_[account](./Glossary.md#account)_
- _[contrib](./Glossary.md#contrib)_ - a term used for optional code contributed by the community.
- _[core](./Glossary.md#core)_ - a term used for the code distributed with Evennia proper
- _[django](./Glossary.md#django)_ - web framework Evennia uses for database access and web integration
- _[field](./Glossary.md#field)_ - a _[typeclass](./Glossary.md#typeclass)_ property representing a database
column
- _[git](./Glossary.md#git)_ - the version-control system we use
- _[github](./Glossary.md#github)_ - the online hosting of our source code
- _[migrate](./Glossary.md#migrate)_ - updating the database schema
- _[multisession mode`](#multisession-mode)_ - a setting defining how users connect to Evennia
- _[object](./Glossary.md#object)_ - Python instance, general term or in-game
_[typeclass](./Glossary.md#typeclass)_
- _[pip](./Glossary.md#pip)_ - the Python installer
- _player_ - the human connecting to the game with their client
- _[puppet](./Glossary.md#puppet)_ - when an [account](./Glossary.md#account) controls an in-game
[object](./Glossary.md#object)
- _[property](./Glossary.md#property)_ - a python property
- _evenv_ - see _[virtualenv](./Glossary.md#virtualenv)_
- _[repository](./Glossary.md#repository)_ - a store of source code + source history
- _[script](./Glossary.md#script)_ - a building block for custom storage, systems and time-keepint
- _[session](./Glossary.md#session)_ - represents one client connection
- _[ticker](./Glossary.md#ticker)_ - Allows to run events on a steady 'tick'
- _[twisted](./Glossary.md#twisted)_ - networking engine responsible for Evennia's event loop and
communications
- _[typeclass](./Glossary.md#typeclass)_ - Evennia's database-connected Python class
- _upstream_ - see _[github](./Glossary.md#github)_
- _[virtualenv](./Glossary.md#virtualenv)_ - a Python program and way to make an isolated Python install
---
## _account_
The term 'account' refers to the [player's](./Glossary.md#account) unique account on the game. It is
represented by the `Account` [typeclass](./Glossary.md#typeclass) and holds things like email, password,
configuration etc.
When a player connects to the game, they connect to their account. The account has *no*
representation in the game world. Through their Account they can instead choose to
[puppet](./Glossary.md#puppet) one (or more, depending on game mode) [Characters](./Glossary.md#character) in
the game.
In the default [multisession mode](Components/Sessions.md#multisession-mode) of Evennia, you immediately start
puppeting a Character with the same name as your Account when you log in - mimicking how older
servers used to work.
## _admin-site_
This usually refers to [Django's](./Glossary.md#django) *Admin site* or database-administration web page
([link to Django docs](https://docs.djangoproject.com/en/2.1/ref/contrib/admin/)). The admin site is
an automatically generated web interface to the database (it can be customized extensively). It's
reachable from the `admin` link on the default Evennia website you get with your server.
## _attribute_
The term _Attribute_ should not be confused with ([properties](./Glossary.md#property) or
[fields](./Glossary.md#field). The `Attribute` represents arbitrary pieces of data that can be attached
to any [typeclassed](./Glossary.md#typeclass) entity in Evennia. Attributes allows storing new persistent
data on typeclasses without changing their underlying database schemas.
[Read more about Attributes here](Components/Attributes.md).
## _channel_
A _Channel_ refers to an in-game communication channel. It's an entity that people subscribe to and
which re-distributes messages between all subscribers. Such subscribers default to being
[Accounts](./Glossary.md#account), for out-of-game communication but could also be [Objects (usually
Characters)](./Glossary.md#character) if one wanted to adopt Channels for things like in-game walkie-
talkies or phone systems. It is represented by the `Channel` typeclass. [You can read more about the
comm system here](Components/Channels.md).
## _character_
The _Character_ is the term we use for the default avatar being [puppeted](./Glossary.md#puppet) by the
[account](./Glossary.md#account) in the game world. It is represented by the `Character` typeclass (which
is a child of [Object](./Glossary.md#object)). Many developers use children of this class to represent
monsters and other NPCs. You can [read more about it here](Components/Objects.md#subclasses-of-object).
## _django_
[Django](https://www.djangoproject.com/) is a professional and very popular Python web framework,
similar to Rails for the Ruby language. It is one of Evennia's central library dependencies (the
other one is [Twisted](./Glossary.md#twisted)). Evennia uses Django for two main things - to map all
database operations to Python and for structuring our web site.
Through Django, we can work with any supported database (SQlite3, Postgres, MySQL ...) using generic
Python instead of database-specific SQL: A database table is represented in Django as a Python class
(called a *model*). An Python instance of such a class represents a row in that table.
There is usually no need to know the details of Django's database handling in order to use Evennia -
it will handle most of the complexity for you under the hood using what we call
[typeclasses](./Glossary.md#typeclass). But should you need the power of Django you can always get it.
Most commonly people want to use "raw" Django when doing more advanced/custom database queries than
offered by Evennia's [default search functions](Howtos/Beginner-Tutorial/Part1/Beginner-Tutorial-Searching-Things.md). One will then need
to read about Django's _querysets_. Querysets are Python method calls on a special form that lets
you build complex queries. They get converted into optimized SQL queries under the hood, suitable
for your current database. [Here is our tutorial/explanation of Django queries](Tutorial-Searching-
For-Objects#queries-in-django).
> By the way, Django (and Evennia) does allow you to fall through and send raw SQL if you really
want to. It's highly unlikely to be needed though; the Django database abstraction is very, very
powerful.
The other aspect where Evennia uses Django is for web integration. On one end Django gives an
infrastructure for wiring Python functions (called *views*) to URLs: the view/function is called
when a user goes that URL in their browser, enters data into a form etc. The return is the web page
to show. Django also offers templating with features such as being able to add special markers in
HTML where it will insert the values of Python variables on the fly (like showing the current player
count on the web page). [Here is one of our tutorials on wiring up such a web page](Add-a-simple-
new-web-page). Django also comes with the [admin site](./Glossary.md#admin-site), which automatically
maps the database into a form accessible from a web browser.
## _core_
This term is sometimes used to represent the main Evennia library code suite, *excluding* its
[contribs](./Glossary.md#contrib) directory. It can sometimes come up in code reviews, such as
> Evennia is game-agnostic but this feature is for a particular game genre. So it does not belong in
core. Better make it a contrib.
## _contrib_
Game-specific code and examples are distributed in evennia's [contribs/](Contribs/Contribs-Overview.md) folder.
This is game-specific, optional code created by the Evennia community.
## _field_
A _field_ or _database field_ in Evennia refers to a [property](./Glossary.md#property) on a
[typeclass](./Glossary.md#typeclass) directly linked to an underlying database column. Only a few fixed
properties per typeclass are database fields but they are often tied to the core functionality of
that base typeclass (for example [Objects](./Glossary.md#object) store its location as a field). In all
other cases, [attributes](./Glossary.md#attribute) are used to add new persistent data to the typeclass.
[Read more about typeclass properties here](Components/Typeclasses.md#about-typeclass-properties).
## _git_
[Git](https://git-scm.com/) is a [version control](https://en.wikipedia.org/wiki/Version_control)
tool. It allows us to track the development of the Evennia code by dividing it into units called
*commits*. A 'commit' is sort of a save-spot - you save the current state of your code and can then
come back to it later if later changes caused problems. By tracking commits we know what 'version'
of the code we are currently using.
Evennia's source code + its source history is jointly called a [repository](./Glossary.md#repository).
This is centrally stored at our online home on [GitHub](./Glossary.md#github). Everyone using or
developing Evennia makes a 'clone' of this repository to their own computer - everyone
automatically gets everything that is online, including all the code history.
> Don't confuse Git and [GitHub](./Glossary.md#github). The former is the version control system. The
latter is a website (run by a company) that allows you to upload source code controlled by Git for
others to see (among other things).
Git allows multiple users from around the world to efficiently collaborate on Evennia's code: People
can make local commits on their cloned code. The commits they do can then be uploaded to GitHub and
reviewed by the Evennia lead devs - and if the changes look ok they can be safely *merged* into the
central Evennia code - and everyone can *pull* those changes to update their local copies.
Developers using Evennia often uses Git on their own games in the same way - to track their changes
and to help collaboration with team mates. This is done completely independently of Evennia's Git
usage.
Common usage (for non-Evennia developers):
- `git clone <github-url>` - clone an online repository to your computer. This is what you do when
you 'download' Evennia. You only need to do this once.
- `git pull` (inside local copy of repository) - sync your local repository with what is online.
> Full usage of Git is way beyond the scope of this glossary. See
[Tutorial - version control](Coding/Version-Control.md) for more info and links to the Git documentation.
## _migrate_
This term is used for upgrading the database structure (it's _schema_ )to a new version. Most often
this is due to Evennia's [upstream](./Glossary.md#github) schema changing. When that happens you need to
migrate that schema to the new version as well. Once you have used [git](./Glossary.md#git) to pull the
latest changes, just `cd` into your game dir and run
evennia migrate
That should be it (see [virtualenv](./Glossary.md#virtualenv) if you get a warning that the `evennia`
command is not available). See also [Updating your game](Setup/Updating-Evennia.md) for more details.
> Technically, migrations are shipped as little Python snippets of code that explains which database
actions must be taken to upgrade from one version of the schema to the next. When you run the
command above, those snippets are run in sequence.
## _multisession mode_
This term refers to the `MULTISESSION_MODE` setting, which has a value of 0 to 3. The mode alters
how players can connect to the game, such as how many Sessions a player can start with one account
and how many Characters they can control at the same time. It is [described in detail
here](Components/Sessions.md#multisession-mode).
## _github_
[Github](https://github.com/evennia) is where Evennia's source code and documentation is hosted.
This online [repository](./Glossary.md#repository) of code we also sometimes refer to as _upstream_.
GitHub is a business, offering free hosting to Open-source projects like Evennia. Despite the
similarity in name, don't confuse GitHub the website with [Git](./Glossary.md#git), the versioning
system. Github hosts Git [repositories](./Glossary.md#repository) online and helps with collaboration and
infrastructure. Git itself is a separate project.
## _object_
In general Python (and other [object-oriented languages](https://en.wikipedia.org/wiki/Object-
oriented_programming)), an `object` is what we call the instance of a *class*. But one of Evennia's
core [typeclasses](./Glossary.md#typeclass) is also called "Object". To separate these in the docs we
try to use `object` to refer to the general term and capitalized `Object` when we refer to the
typeclass.
The `Object` is a typeclass that represents all *in-game* entities, including
[Characters](./Glossary.md#character), rooms, trees, weapons etc. [Read more about Objects here](Components/Objects.md).
## _pip_
_[pip](https://pypi.org/project/pip/)_ comes with Python and is the main tool for installing third-
party Python packages from the web. Once a python package is installed you can do `import
<packagename>` in your Python code.
Common usage:
- `pip install <package-name>` - install the given package along with all its dependencies.
- `pip search <name>` - search Python's central package repository [PyPi](https://pypi.org/) for a
package of that name.
- `pip install --upgrade <package_name>` - upgrade a package you already have to the latest version.
- `pip install <packagename>==1.5` - install exactly a specific package version.
- `pip install <folder>` - install a Python package you have downloaded earlier (or cloned using
git).
- `pip install -e <folder>` - install a local package by just making a soft link to the folder. This
means that if the code in `<folder>` changes, the installed Python package is immediately updated.
If not using `-e`, one would need to run `pip install --upgrade <folder>` every time to make the
changes available when you import this package into your code. Evennia is installed this way.
For development, `pip` is usually used together with a [virtualenv](./Glossary.md#virtualenv) to install
all packages and dependencies needed for a project in one, isolated location on the hard drive.
## _puppet_
An [account](./Glossary.md#account) can take control and "play as" any [Object](./Glossary.md#object). When
doing so, we call this _puppeting_, (like [puppeteering](https://en.wikipedia.org/wiki/Puppeteer)).
Normally the entity being puppeted is of the [Character](./Glossary.md#character) subclass but it does
not have to be.
## _property_
A _property_ is a general term used for properties on any Python object. The term also sometimes
refers to the `property` built-in function of Python ([read more here](https://www.python-
course.eu/python3_properties.php)). Note the distinction between properties,
[fields](./Glossary.md#field) and [Attributes](./Glossary.md#attribute).
## _repository_
A _repository_ is a version control/[git](./Glossary.md#git) term. It represents a folder containing
source code plus its versioning history.
> In Git's case, that history is stored in a hidden folder `.git`. If you ever feel the need to look
into this folder you probably already know enough Git to know why.
The `evennia` folder you download from us with `git clone` is a repository. The code on
[GitHub](./Glossary.md#github) is often referred to as the 'online repository' (or the _upstream_
repository). If you put your game dir under version control, that of course becomes a repository as
well.
## _script_
When we refer to _Scripts_, we generally refer to the `Script` [typeclass](Components/Typeclasses.md). Scripts are
the mavericks of Evennia - they are like [Objects](./Glossary.md#object) but without any in-game
existence. They are useful as custom places to store data but also as building blocks in persistent
game systems. Since the can be initialized with timing capabilities they can also be used for long-
time persistent time keeping (for fast updates other types of timers may be better though).
[Read more about Scripts here](Components/Scripts.md)
## _session_
A [Session](Components/Sessions.md) is a Python object representing a single client connection to the server. A
given human player could connect to the game from different clients and each would get a Session
(even if you did not allow them to actually log in and get access to an
[account](./Glossary.md#account)).
Sessions are _not_ [typeclassed](./Glossary.md#typeclass) and has no database persistence. But since they
always exist (also when not logged in), they share some common functionality with typeclasses that
can be useful for certain game states.
## _tag_
A [Tag](Components/Tags.md) is used to group and categorize other database entitiess together in an efficient way
so they can be efficiently searched later.
## _ticker_
The [Ticker handler](Components/TickerHandler.md) runs Evennia's optional 'ticker' system. In other engines, such
as [DIKU](https://en.wikipedia.org/wiki/DikuMUD), all game events are processed only at specific
intervals called 'ticks'. Evennia has no such technical limitation (events are processed whenever
needed) but using a fixed tick can still be useful for certain types of game systems, like combat.
Ticker Handler allows you to emulate any number of tick rates (not just one) and subscribe actions
to be called when those ticks come around.
## _typeclass_
The [typeclass](Components/Typeclasses.md) is an Evennia-specific term. A typeclass allows developers to work with
database-persistent objects as if they were normal Python objects. It makes use of specific
[Django](./Glossary.md#django) features to link a Python class to a database table. Sometimes we refer to
such code entities as _being typeclassed_.
Evennia's main typeclasses are [Account](./Glossary.md#account), [Object](./Glossary.md#object),
[Script](./Glossary.md#script) and [Channel](./Glossary.md#channel). Children of the base class (such as
[Character](./Glossary.md#character)) will use the same database table as the parent, but can have vastly
different Python capabilities (and persistent features through [Attributes](./Glossary.md#attribute) and
[Tags](./Glossary.md#tag). A typeclass can be coded and treated pretty much like any other Python class
except it must inherit (at any distance) from one of the base typeclasses. Also, creating a new
instance of a typeclass will add a new row to the database table to which it is linked.
The [core](./Glossary.md#core) typeclasses in the Evennia library are all named `DefaultAccount`,
`DefaultObject` etc. When you initialize your [game dir] you automatically get empty children of
these, called `Account`, `Object` etc that you can start working with.
## _twisted_
[Twisted](https://twistedmatrix.com/trac/) is a heavy-duty asynchronous networking engine. It is one
of Evennia's two major library dependencies (the other one is [Django](./Glossary.md#django)). Twisted is
what "runs" Evennia - it handles Evennia's event loop. Twisted also has the building blocks we need
to construct network protocols and communicate with the outside world; such as our MUD-custom
version of Telnet, Telnet+SSL, SSH, webclient-websockets etc. Twisted also runs our integrated web
server, serving the Django-based website for your game.
## _virtualenv_
The standard [virtualenv](https://virtualenv.pypa.io/en/stable/) program comes with Python. It is
used to isolate all Python packages needed by a given Python project into one folder (we call that
folder `evenv` but it could be called anything). A package environment created this way is usually
referred to as "a virtualenv". If you ever try to run the `evennia` program and get an error saying
something like "the command 'evennia' is not available" - it's probably because your virtualenv is
not 'active' yet (see below).
Usage:
- `python3.10 -m venv evenv` - initialize a new virtualenv-folder `evenv` in the current
location. You can call this whatever you like. The Python-version you use for this call will be the one used
for everything inside the virtualenv.
- `source evenv/bin/activate` (linux/mac) or `evenv\Scripts\activate`(windows) - this activates the
virtualenv.
- `deactivate` - turn off the currently activated virtualenv.
A virtualenv is 'activated' only for the console/terminal it was started in, but it's safe to
activate the same virtualenv many times in different windows if you want. Once activated, all Python
packages now installed with [pip](./Glossary.md#pip) will install to `evenv` rather than to a global
location like `/usr/local/bin` or `C:\Program Files`.
> Note that if you have root/admin access you *could* install Evennia globally just fine, without
using a virtualenv. It's strongly discouraged and considered bad practice though. Experienced Python
developers tend to rather create one new virtualenv per project they are working on, to keep the
varying installs cleanly separated from one another.
When you execute Python code within this activated virtualenv, *only* those packages installed
within will be possible to `import` into your code. So if you installed a Python package globally on
your computer, you'll need to install it again in your virtualenv.
> Virtualenvs *only* deal with Python programs/packages. Other programs on your computer couldn't
care less if your virtualenv is active or not. So you could use `git` without the virtualenv being
active, for example.
When your virtualenv is active you should see your console/terminal prompt change to
(evenv) ...
... or whatever name you gave the virtualenv when you initialized it.
> We sometimes say that we are "in" the virtualenv when it's active. But just to be clear - you
never have to actually `cd` into the `evenv` folder. You can activate it from anywhere and will
still be considered "in" the virtualenv wherever you go until you `deactivate` or close the
console/terminal.
So, when do you *need* to activate my virtualenv? If the virtualenv is not active, none of the Python
packages/programs you installed in it will be available to you. So at a minimum, *it needs to be
activated whenever you want to use the `evennia` command* for any reason.

View file

@ -6,7 +6,7 @@ program. If the `evennia` program is not available on the command line you must
Evennia as described on the [Installation](./Installation.md) page.
```{sidebar} evennia not found?
If you ever try the `evennia` command and get an error complaining that the command is not available, make sure your [virtualenv](../Glossary.md#virtualenv) is active. On Windows you may need to to run `py -m evennia` once first.
If you ever try the `evennia` command and get an error complaining that the command is not available, make sure your [virtualenv](./Installation-Git.md#virtualenv) is active. On Windows you may need to to run `py -m evennia` once first.
```
Below are described the various management options. Run
@ -21,7 +21,7 @@ to give you a menu with options.
## Starting Evennia
Evennia consists of two components, the Evennia [Portal and Server](../Components/Portal-And-Server.md). Briefly, the *Server* is what is running the mud. It handles all game-specific things but doesn't care exactly how players connect, only that they have. The *Portal* is a gateway to which players connect. It knows everything about telnet, ssh, webclient protocols etc but very little about the game. Both are required for a functioning game.
Evennia consists of two components, the Evennia [Portal and Server](../Components/Portal-And-Server.md). Briefly, the *Server* is what is running the mud. It handles all game-specific things but doesn't care exactly how players connect, only that they have. The *Portal* is a gateay to which players connect. It knows everything about telnet, ssh, webclient protocols etc but very little about the game. Both are required for a functioning game.
evennia start

View file

@ -30,9 +30,9 @@ This is the manual of [Evennia](https://www.evennia.com), the open source Python
## Developing with Evennia
- [Coding with Evennia](Coding/Coding-Overview.md) - coding and development hints and resources
- [Core components](Components/Components-Overview.md) - the core building blocks of Evennia
- [Concepts](Concepts/Concepts-Overview.md) - larger-scale concepts and features
- [Coding](Coding/Coding-Overview.md) - coding and development hints and resources
- [Core Concepts](Concepts/Concepts-Overview.md) - larger-scale concepts and features
- [API](./Evennia-API.md) - the full API-reference, generated from source
- [Default Commands](Components/Default-Commands.md) - list of game commands included out of the box
@ -77,7 +77,6 @@ Links
```{toctree}
:hidden:
Glossary
Evennia-API
Licensing
Unimplemented

View file

@ -2,12 +2,11 @@
EvAdventure character generation.
"""
from django.conf import settings
from evennia import create_object
from evennia.objects.models import ObjectDB
from evennia.prototypes.spawner import spawn
from evennia.utils.evmenu import EvMenu
from evennia.objects.models import ObjectDB
from django.conf import settings
from .characters import EvAdventureCharacter
from .random_tables import chargen_tables
@ -339,5 +338,11 @@ def start_chargen(caller, session=None):
# this generates all random components of the character
tmp_character = TemporaryCharacterSheet()
EvMenu(caller, menutree, startnode="node_chargen", session=session, startnode_input=('sgsg', {"tmp_character":tmp_character}))
EvMenu(
caller,
menutree,
startnode="node_chargen",
session=session,
startnode_input=("sgsg", {"tmp_character": tmp_character}),
)

View file

@ -5,10 +5,9 @@ Test chargen.
from unittest.mock import MagicMock, patch
from parameterized import parameterized
from evennia import create_object
from evennia.utils.test_resources import BaseEvenniaTest
from parameterized import parameterized
from .. import chargen, enums, objects
@ -45,9 +44,11 @@ class EvAdventureCharacterGenerationTest(BaseEvenniaTest):
def test_apply(self, mock_spawn):
gambeson = create_object(objects.EvAdventureArmor, key="gambeson")
mock_spawn.return_value = gambeson
mock_spawn.return_value = [gambeson]
account = MagicMock()
account.id = 2222
character = self.chargen.apply()
character = self.chargen.apply(account)
self.assertIn("Herbalist", character.db.desc)
self.assertEqual(