Merge branch 'evennia:main' into spawnupdate-fix

This commit is contained in:
Tegiminis 2023-01-04 16:54:31 -08:00 committed by GitHub
commit 7c81d37e36
47 changed files with 422 additions and 373 deletions

View file

@ -1,27 +0,0 @@
---
name: Bug report (branch-develop)
about: Use this to report errors in the Evennia `develop` branch
title: "[BUG - Develop] (Enter a brief description here)"
labels: bug, branch-develop, needs-triage
assignees: ''
---
#### Describe the bug
<!--(This is for bugs in the develop-branch only. Make sure you test with the latest version.)-->
#### To Reproduce
Steps to reproduce the behavior:
1.
2.
3.
4. See error
#### Expected behavior
<!--(Replace with a clear and concise description of what you expected to happen.-->)
#### Develop-branch commit
<!--(The commit-hash. If unsure, run `evennia -v` or get the first few lines of the `about` command in-game.)-->
#### Additional context
<!--(Replace with any other context about the problem, or ideas on how to solve.)-->

View file

@ -4,12 +4,12 @@ name: documentation
on: on:
push: push:
branches: [ master, develop ] branches: [ main, develop ]
paths: paths:
- 'docs/**' - 'docs/**'
- 'evennia/contrib/**' - 'evennia/contrib/**'
pull_request: pull_request:
branches: [ master, develop ] branches: [ main, develop ]
paths: paths:
- 'docs/**' - 'docs/**'
- 'evennia/contrib/**' - 'evennia/contrib/**'
@ -48,8 +48,8 @@ jobs:
cd gamedir cd gamedir
evennia migrate evennia migrate
- name: Build and deploy docs (only from master/develop branch) - name: Build and deploy docs (only from main/develop branch)
if: ${{ github.ref == 'refs/heads/develop' || github.ref == 'refs/heads/master'}} if: ${{ github.ref == 'refs/heads/develop' || github.ref == 'refs/heads/main'}}
run: | run: |
git config --global user.email "docbuilder@evennia.com" git config --global user.email "docbuilder@evennia.com"
git config --global user.name "Evennia docbuilder action" git config --global user.name "Evennia docbuilder action"

View file

@ -1,37 +0,0 @@
# This Evennia workflow will deploy the Evennia package automatically to
# pypi if vNN tag was given.
#
name: publish-evennia
on:
push:
tags: ['v*']
jobs:
build-and-publish:
name: Build and publish Evennia to PyPi
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@master
- name: Set up Python 3.11
uses: actions/setup-python@v3
with:
python-version: "3.11"
- name: Install build
run: |
python -m pip install --upgrade pip
python -m pip install build --user
- name: Build binary wheel and source tarball
run: |
python -m build --sdist --wheel --outdir dist/ .
- name: Publish Evennia PyPi (on tag)
if: startsWith(github.ref, 'refs/tags')
uses: pypa/gh-action-pypi-publish@release/v1
with:
password: ${{ secrets.PYPI_API_TOKEN }}

View file

@ -26,6 +26,12 @@ jobs:
TESTING_DB: "sqlite3" TESTING_DB: "sqlite3"
coverage-test: true coverage-test: true
timeout-minutes: 35
env:
UNIT_TEST_SETTINGS: "--settings=settings --keepdb --parallel 4 --timing"
COVERAGE_TEST_SETTINGS: "--settings=settings --timing"
steps: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v3
@ -67,12 +73,7 @@ jobs:
if: ${{ ! matrix.coverage-test }} if: ${{ ! matrix.coverage-test }}
working-directory: testing_mygame working-directory: testing_mygame
run: | run: |
evennia test \ evennia test ${{ env.UNIT_TEST_SETTINGS }} evennia
--settings=settings \
--keepdb \
--parallel 4 \
--timing \
evennia
# OBS - it's important to not run the coverage tests with --parallel, it messes up the coverage # OBS - it's important to not run the coverage tests with --parallel, it messes up the coverage
# calculation! # calculation!
@ -80,13 +81,8 @@ jobs:
if: ${{ matrix.coverage-test }} if: ${{ matrix.coverage-test }}
working-directory: testing_mygame working-directory: testing_mygame
run: | run: |
coverage run \ coverage run --rcfile=../pyproject.toml ../bin/unix/evennia test ${{ env.COVERAGE_TEST_SETTINGS }} evennia
--source=evennia \ coverage combine
--omit=*/migrations/*,*/urls.py,*/test*.py,*.sh,*.txt,*.md,*.pyc,*.service \
../bin/unix/evennia test \
--settings=settings \
--timing \
evennia
coverage xml coverage xml
coverage --version coverage --version
coverage report | grep TOTAL coverage report | grep TOTAL
@ -107,7 +103,7 @@ jobs:
name: Deploy Docker Image name: Deploy Docker Image
needs: test needs: test
runs-on: ubuntu-latest runs-on: ubuntu-latest
if: ${{ github.repository == 'evennia/evennia' && (github.ref == 'refs/heads/master' || github.ref == 'refs/heads/develop') }} if: ${{ github.repository == 'evennia/evennia' && (github.ref == 'refs/heads/main' || github.ref == 'refs/heads/develop') }}
steps: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v3
@ -123,9 +119,9 @@ jobs:
username: ${{ secrets.DOCKERHUB_USERNAME }} username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }} password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Build and push for master - name: Build and push for main
if: ${{ github.ref == 'refs/heads/master' }} if: ${{ github.ref == 'refs/heads/main' }}
id: docker_build_master id: docker_build_main
uses: docker/build-push-action@v3 uses: docker/build-push-action@v3
with: with:
push: true push: true

45
.release.sh Executable file
View file

@ -0,0 +1,45 @@
# Release helper
VERSION=$(cat evennia/VERSION.txt)
echo "This will release Evennia $VERSION (based on evennia/VERSION.txt)."
echo "Before continuing:"
echo " 1. Make sure you have Evennia upload credentials for Github (tagging) and PyPi (main package)."
echo " 2. Update CHANGELOG.md."
echo " 3. Run 'make local' in docs/ to update dynamic docs and autodocstrings (may have to run twice)."
echo " 4. Make sure VERSION.txt and pyproject.toml both show version $VERSION."
echo " 5. Make sure all changes are committed, e.g. as 'Evennia 1.x.x bug-fix release' (un-staged files will be wiped)."
echo " 6. Make sure all unit tests pass!"
read -p "Continue? [Y/n]> " yn
case $yn in
[nN] ) echo "Aborting.";
exit;;
* ) echo "Starting release ...";;
esac
# clean and build the pypi distribution
echo "Preparing and Building PyPi package ..."
rm -Rf dist/
git clean -xdf
pip install --upgrade pip
pip install build twine
python -m build --sdist --wheel --outdir dist/ .
echo "Uploading PyPi package (requires PyPi credentials) ..."
python -m twine upload dist/*
# tag the latest git commit
echo "Creating and pushing release tag tag v$VERSION (requires GitHub credentials)..."
git tag -a v$VERSION -m "Evennia release v$VERSION"
git push --tags
echo "... Release complete."
echo ""
echo "Post-release actions:"
echo " 1. Make sure to push all commits."
echo " 2. Update github discussions to report on release."
echo " 2. Make post in discord #announcements channel pointing to discussion post."
echo " 3. Any other announcements as needed."

View file

@ -1,11 +1,22 @@
# Changelog # Changelog
### Evennia 1.0 ### Evennia 1.0.2
Dec 21, 2022
2019-2022 develop branch Bug fix release. Fix more issues with discord bot reconnecting. Some doc
updates.
Changed to using `main` branch to follow github standard. Old `master` branch remains ### Evennia 1.0.1
for now but will not be used anymore, so as to not break installs during transition. Dec 7, 2022
Bug fix release. Main issue was reconnect bug for discord bot.
## Evennia 1.0
2019-2022
_Changed to using `main` branch to follow github standard. Old `master` branch remains
for now but will not be used anymore, so as to not break installs during transition._
Increase requirements: Django 4.1+, Twisted 22.10+ Python 3.10, 3.11. PostgreSQL 11+. Increase requirements: Django 4.1+, Twisted 22.10+ Python 3.10, 3.11. PostgreSQL 11+.

View file

@ -13,7 +13,7 @@ default:
@echo " make test - run evennia test suite with all default values." @echo " make test - run evennia test suite with all default values."
@echo " make tests=evennia.path test - run only specific test or tests." @echo " make tests=evennia.path test - run only specific test or tests."
@echo " make testp - run test suite using multiple cores." @echo " make testp - run test suite using multiple cores."
@echo " make publish - publish evennia to pypi (requires pypi credentials) @echo " make release - publish evennia to pypi (requires pypi credentials)
install: install:
pip install -e . pip install -e .
@ -44,10 +44,8 @@ testp:
evennia migrate;\ evennia migrate;\
evennia test --keepdb --parallel 4 $(TESTS);\ evennia test --keepdb --parallel 4 $(TESTS);\
version:
echo $(VERSION)
release: release:
rm -Rf dist/ ./.release.sh
git clean -xdf
pip install --upgrade pip
pip install build twine
python -m build --sdist --wheel --outdir dist/ .
python -m twine upload dist/*

View file

@ -1,5 +1,5 @@
# Evennia MUD/MU\* Creation System ![][logo] # Evennia MUD/MU\* Creation System ![][logo]
[![Build Status][unittestciimg]][unittestcilink] [![Coverage Status][coverimg]][coverlink] [![Pypi Version][pypibadge]][pypilink] [![unittestciimg]][unittestcilink] [![Coverage Status][coverimg]][coverlink] [![Pypi Version][pypibadge]][pypilink]
[Evennia][homepage] is a modern library for creating [online multiplayer text [Evennia][homepage] is a modern library for creating [online multiplayer text
@ -48,8 +48,8 @@ Welcome!
[logo]: https://github.com/evennia/evennia/blob/master/evennia/web/website/static/website/images/evennia_logo.png [logo]: https://github.com/evennia/evennia/blob/master/evennia/web/website/static/website/images/evennia_logo.png
[unittestciimg]: https://github.com/evennia/evennia/workflows/test-suite/badge.svg [unittestciimg]: https://github.com/evennia/evennia/workflows/test-suite/badge.svg
[unittestcilink]: https://github.com/evennia/evennia/actions?query=workflow%3Atest-suite [unittestcilink]: https://github.com/evennia/evennia/actions?query=workflow%3Atest-suite
[coverimg]: https://coveralls.io/repos/github/evennia/evennia/badge.svg?branch=master [coverimg]: https://coveralls.io/repos/github/evennia/evennia/badge.svg?branch=main
[coverlink]: https://coveralls.io/github/evennia/evennia?branch=master [coverlink]: https://coveralls.io/github/evennia/evennia?branch=main
[pypibadge]: https://img.shields.io/pypi/v/evennia?color=blue [pypibadge]: https://img.shields.io/pypi/v/evennia?color=blue
[pypilink]: https://pypi.org/project/evennia/ [pypilink]: https://pypi.org/project/evennia/
[introduction]: https://www.evennia.com/docs/latest/Evennia-Introduction.html [introduction]: https://www.evennia.com/docs/latest/Evennia-Introduction.html

View file

@ -1,11 +1,22 @@
# Changelog # Changelog
### Evennia 1.0 ### Evennia 1.0.2
Dec 21, 2022
2019-2022 develop branch Bug fix release. Fix more issues with discord bot reconnecting. Some doc
updates.
Changed to using `main` branch to follow github standard. Old `master` branch remains ### Evennia 1.0.1
for now but will not be used anymore, so as to not break installs during transition. Dec 7, 2022
Bug fix release. Main issue was reconnect bug for discord bot.
## Evennia 1.0
2019-2022
_Changed to using `main` branch to follow github standard. Old `master` branch remains
for now but will not be used anymore, so as to not break installs during transition._
Increase requirements: Django 4.1+, Twisted 22.10+ Python 3.10, 3.11. PostgreSQL 11+. Increase requirements: Django 4.1+, Twisted 22.10+ Python 3.10, 3.11. PostgreSQL 11+.

View file

@ -2,6 +2,8 @@
This summarizes the changes. See the [Changelog](./Changelog.md) for the full list. This summarizes the changes. See the [Changelog](./Changelog.md) for the full list.
- Main development now on `main` branch. `master` branch remains, but will not be updated anymore.
## Minimum requirements ## Minimum requirements
- Python 3.10 is now required minimum. Ubuntu LTS now installs with 3.10. Evennia 1.0 is also tested with Python 3.11 - this is the recommended version for Linux/Mac. Windows users may want to stay on Python 3.10 unless they are okay with installing a C++ compiler. - Python 3.10 is now required minimum. Ubuntu LTS now installs with 3.10. Evennia 1.0 is also tested with Python 3.11 - this is the recommended version for Linux/Mac. Windows users may want to stay on Python 3.10 unless they are okay with installing a C++ compiler.

View file

@ -43,7 +43,7 @@ forces Evennia to use this settings file over the default one.
You can also test specific things by giving their path You can also test specific things by giving their path
evennia test --settings settings.py .world.tests.YourTest evennia test --settings settings.py world.tests.YourTest
## Writing new unit tests ## Writing new unit tests
@ -106,15 +106,15 @@ To test this, run
to run the entire test module to run the entire test module
evennia test --settings setings.py .world.tests evennia test --settings setings.py world.tests
or a specific class: or a specific class:
evennia test --settings settings.py .world.tests.TestObj evennia test --settings settings.py world.tests.TestObj
You can also run a specific test: You can also run a specific test:
evennia test --settings settings.py .world.tests.TestObj.test_alternative_call evennia test --settings settings.py world.tests.TestObj.test_alternative_call
You might also want to read the [Python documentation for the unittest module](https://docs.python.org/library/unittest.html). You might also want to read the [Python documentation for the unittest module](https://docs.python.org/library/unittest.html).

View file

@ -295,7 +295,7 @@ You can't do `git push upstream` unless you have write-access to the upstream Ev
This should be done in your fork of Evennia. You should _always_ do this in a _separate git branch_ based off the Evennia branch you want to improve. This should be done in your fork of Evennia. You should _always_ do this in a _separate git branch_ based off the Evennia branch you want to improve.
git checkout main (or develop) git checkout main (or develop)
git branch - b myfixbranch git branch -b myfixbranch
Now fix whatever needs fixing. Abide by the [Evennia code style](./Evennia-Code-Style.md). You can `git commit` commit your changes along the way as normal. Now fix whatever needs fixing. Abide by the [Evennia code style](./Evennia-Code-Style.md). You can `git commit` commit your changes along the way as normal.

View file

@ -19,7 +19,7 @@ updated after Sept 2022 will be missing some translations.
+---------------+----------------------+--------------+ +---------------+----------------------+--------------+
| es | Spanish | Aug 2019 | | es | Spanish | Aug 2019 |
+---------------+----------------------+--------------+ +---------------+----------------------+--------------+
| fr | French | Mar 2022 | | fr | French | Dec 2022 |
+---------------+----------------------+--------------+ +---------------+----------------------+--------------+
| it | Italian | Oct 2022 | | it | Italian | Oct 2022 |
+---------------+----------------------+--------------+ +---------------+----------------------+--------------+

View file

@ -42,7 +42,7 @@ is being combined instead):
See the [sword example](evennia.contrib.game_systems.crafting.example_recipes) for an example See the [sword example](evennia.contrib.game_systems.crafting.example_recipes) for an example
of how to design a recipe tree for crafting a sword from base elements. of how to design a recipe tree for crafting a sword from base elements.
## Intallation and Usage ## Installation and Usage
Import the `CmdCraft` command from evennia/contrib/crafting/crafting.py and Import the `CmdCraft` command from evennia/contrib/crafting/crafting.py and
add it to your Character cmdset. Reload and the `craft` command will be add it to your Character cmdset. Reload and the `craft` command will be

View file

@ -16,11 +16,12 @@ In more detail, in `mygame/commands/default_cmdsets.py`:
```python ```python
... ...
from evennia.contrib import extended_room # <--- from evennia.contrib.grid import extended_room # <---
class CharacterCmdset(default_cmds.Character_CmdSet): class CharacterCmdset(default_cmds.CharacterCmdSet):
... ...
def at_cmdset_creation(self): def at_cmdset_creation(self):
super().at_cmdset_creation()
... ...
self.add(extended_room.ExtendedRoomCmdSet) # <--- self.add(extended_room.ExtendedRoomCmdSet) # <---
@ -28,7 +29,10 @@ class CharacterCmdset(default_cmds.Character_CmdSet):
Then reload to make the new commands available. Note that they only work Then reload to make the new commands available. Note that they only work
on rooms with the typeclass `ExtendedRoom`. Create new rooms with the right on rooms with the typeclass `ExtendedRoom`. Create new rooms with the right
typeclass or use the `typeclass` command to swap existing rooms. typeclass or use the `typeclass` command to swap existing rooms. Note that since
this contrib overrides the `look` command, you will need to add the
`extended_room.ExtendedRoomCmdSet` to the default character cmdset *after*
super().at_cmdset_creation(), or it will be overridden by the default look.
## Features ## Features

View file

@ -64,6 +64,7 @@ Start small. Evennia's [Beginner tutorial](Howtos/Beginner-Tutorial/Beginner-Tut
```{sidebar} ```{sidebar}
See also our [link page](./Links.md) for some reading suggestions. See also our [link page](./Links.md) for some reading suggestions.
``` ```
While Python is considered a very easy programming language to get into, you do have a learning curve to climb if you are new to programming. The beginner-tutorial has a [basic introduction to Python](Howtos/Beginner-Tutorial/Part1/Beginner-Tutorial-Python-basic-introduction.md), but if you are completely new, you should probably also sit down with a full Python beginner's tutorial at some point. There are plenty of them on the web if you look around. While Python is considered a very easy programming language to get into, you do have a learning curve to climb if you are new to programming. The beginner-tutorial has a [basic introduction to Python](Howtos/Beginner-Tutorial/Part1/Beginner-Tutorial-Python-basic-introduction.md), but if you are completely new, you should probably also sit down with a full Python beginner's tutorial at some point. There are plenty of them on the web if you look around.
To code your dream game in Evennia you don't need to be a Python guru, but you do need to be able to read example code containing at least these basic Python features: To code your dream game in Evennia you don't need to be a Python guru, but you do need to be able to read example code containing at least these basic Python features:

View file

@ -113,7 +113,7 @@ class LivingMixin:
healed = min(damage, hp) healed = min(damage, hp)
self.hp += healed self.hp += healed
self.msg("You heal for {healed} HP.") self.msg(f"You heal for {healed} HP.")
def at_pay(self, amount): def at_pay(self, amount):
"""When paying coins, make sure to never detract more than we have""" """When paying coins, make sure to never detract more than we have"""

View file

@ -92,6 +92,7 @@ If `localhost` doesn't work when trying to connect to your local game, try `127.
instead. Some MUD clients on Windows does not appear to understand the alias `localhost`. instead. Some MUD clients on Windows does not appear to understand the alias `localhost`.
- Some Windows users get an error installing the Twisted 'wheel'. A wheel is a pre-compiled binary - Some Windows users get an error installing the Twisted 'wheel'. A wheel is a pre-compiled binary
package for Python. A common reason for this error is that you are using a 32-bit version of Python, but Twisted has not yet uploaded the latest 32-bit wheel. Easiest way to fix this is to install a slightly older Twisted version. So if, say, version `22.1` failed, install `22.0` manually with `pip install twisted==22.0`. Alternatively you could check that you are using the 64-bit version of Python and uninstall any 32bit one. If so, you must then `deactivate` the virtualenv, delete the `evenv` folder and recreate it anew with your new Python. package for Python. A common reason for this error is that you are using a 32-bit version of Python, but Twisted has not yet uploaded the latest 32-bit wheel. Easiest way to fix this is to install a slightly older Twisted version. So if, say, version `22.1` failed, install `22.0` manually with `pip install twisted==22.0`. Alternatively you could check that you are using the 64-bit version of Python and uninstall any 32bit one. If so, you must then `deactivate` the virtualenv, delete the `evenv` folder and recreate it anew with your new Python.
- If you've done a git installation, and your server won't start with an error message like `AttributeError: module 'evennia' has no attribute '_init'`, it may be a python path issue. In a terminal, cd to `(your python directory)\site-packages` and run the command `echo "C:\absolute\path\to\evennia" > local-vendors.pth`. Open the created file in your favorite IDE and make sure it is saved with *UTF-8* encoding and not *UTF-8 with BOM*.
- If your server won't start, with no error messages (and no log files at all when starting from - If your server won't start, with no error messages (and no log files at all when starting from
scratch), try to start with `evennia ipstart` instead. If you then see an error about `system cannot find the path specified`, it may be that the file `evennia\evennia\server\twistd.bat` has the wrong path to the `twistd` executable. This file is auto-generated, so try to delete it and then run `evennia start` to rebuild it and see if it works. If it still doesn't work you need to open it in a text editor like Notepad. It's just one line containing the path to the `twistd.exe` executable as determined by Evennia. If you installed Twisted in a non-standard location this might be wrong and you should update the line to the real location. scratch), try to start with `evennia ipstart` instead. If you then see an error about `system cannot find the path specified`, it may be that the file `evennia\evennia\server\twistd.bat` has the wrong path to the `twistd` executable. This file is auto-generated, so try to delete it and then run `evennia start` to rebuild it and see if it works. If it still doesn't work you need to open it in a text editor like Notepad. It's just one line containing the path to the `twistd.exe` executable as determined by Evennia. If you installed Twisted in a non-standard location this might be wrong and you should update the line to the real location.
- Some users have reported issues with Windows WSL and anti-virus software during Evennia - Some users have reported issues with Windows WSL and anti-virus software during Evennia

View file

@ -16,8 +16,8 @@ Prior to 1.0, all Evennia installs were [Git-installs](./Installation-Git.md). T
- Make a _new_ `evenv` virtualenv (see the [virtualenv instructions](./Installation-Git.md#virtualenv) for help) and make sure it's active - Make a _new_ `evenv` virtualenv (see the [virtualenv instructions](./Installation-Git.md#virtualenv) for help) and make sure it's active
- `cd` into your `evennia/` root folder (you want to be where you see the `docs/` and `bin/` directories as well as a nested `evennia/` folder) - `cd` into your `evennia/` root folder (you want to be where you see the `docs/` and `bin/` directories as well as a nested `evennia/` folder)
- `git pull` - `git pull`
- `git checkout main` (the `master`) - `git checkout main` (instead of `master` which was used for `0.9.5`)
- `pip install -e --upgrade .` - `pip install --upgrade -e .`
- If you want the optional extra libs (needed by some contribs), do `pip install -e --upgrade .[extra]` - If you want the optional extra libs (needed by some contribs), do `pip install -e --upgrade .[extra]`
- Test that you can run the `evennia` command. - Test that you can run the `evennia` command.

View file

@ -328,7 +328,7 @@ Evennia users:
| [Amazon Lightsail][9] | Cloud | $5/month | Free first month. AWS's "fixed cost" offering.| | [Amazon Lightsail][9] | Cloud | $5/month | Free first month. AWS's "fixed cost" offering.|
| [Azure App Services][12] | Cloud | Free | Free tier with limited regions for hobbyists. | | [Azure App Services][12] | Cloud | Free | Free tier with limited regions for hobbyists. |
| [Huawei Cloud][13] | Cloud | on demand | Similar to Amazon. Free 12-month tier with limited regions. | | [Huawei Cloud][13] | Cloud | on demand | Similar to Amazon. Free 12-month tier with limited regions. |
| [Host1Plus][5] | VPS & Cloud | $4/month | $4-$8/month depending on length of sign-up period. | [Heficed][5] | VPS & Cloud | $6/month | $6/month for a 1GB ram server. |
| [Scaleway][6] | Cloud | &euro;3/month / on-demand | EU based (Paris, Amsterdam). Smallest option provides 2GB RAM. | | [Scaleway][6] | Cloud | &euro;3/month / on-demand | EU based (Paris, Amsterdam). Smallest option provides 2GB RAM. |
| [Prgmr][10] | VPS | $5/month | 1 month free with a year prepay. You likely want some experience with servers with this option as they don't have a lot of support.| | [Prgmr][10] | VPS | $5/month | 1 month free with a year prepay. You likely want some experience with servers with this option as they don't have a lot of support.|
| [Linode][11] | Cloud | $5/month / on-demand | Multiple regions. Smallest option provides 1GB RAM| | [Linode][11] | Cloud | $5/month / on-demand | Multiple regions. Smallest option provides 1GB RAM|
@ -340,7 +340,7 @@ Evennia users:
[2]: https://www.digitalocean.com/pricing [2]: https://www.digitalocean.com/pricing
[3]: https://aws.amazon.com/pricing/ [3]: https://aws.amazon.com/pricing/
[4]: https://www.genesismuds.com/ [4]: https://www.genesismuds.com/
[5]: https://www.host1plus.com/ [5]: https://www.heficed.com/
[6]: https://www.scaleway.com/ [6]: https://www.scaleway.com/
[7]: https://lowendbox.com/ [7]: https://lowendbox.com/
[8]: https://www.lowendtalk.com [8]: https://www.lowendtalk.com

View file

@ -1 +1 @@
1.0-dev 1.0.2

View file

@ -22,7 +22,6 @@ import time
from codecs import lookup as codecs_lookup from codecs import lookup as codecs_lookup
from django.conf import settings from django.conf import settings
from evennia.server.sessionhandler import SESSIONS from evennia.server.sessionhandler import SESSIONS
from evennia.utils import create, logger, search, utils from evennia.utils import create, logger, search, utils
@ -191,7 +190,8 @@ class CmdCharCreate(COMMAND_DEFAULT_CLASS):
elif not new_character.db.desc: elif not new_character.db.desc:
new_character.db.desc = "This is a character." new_character.db.desc = "This is a character."
self.msg( self.msg(
f"Created new character {new_character.key}. Use |wic {new_character.key}|n to enter the game as this character." f"Created new character {new_character.key}. Use |wic {new_character.key}|n to enter"
" the game as this character."
) )
logger.log_sec( logger.log_sec(
f"Character Created: {new_character} (Caller: {account}, IP: {self.session.address})." f"Character Created: {new_character} (Caller: {account}, IP: {self.session.address})."
@ -317,11 +317,13 @@ class CmdIC(COMMAND_DEFAULT_CLASS):
if account.db._playable_characters: if account.db._playable_characters:
# look at the playable_characters list first # look at the playable_characters list first
character_candidates.extend( character_candidates.extend(
account.search( utils.make_iter(
self.args, account.search(
candidates=account.db._playable_characters, self.args,
search_object=True, candidates=account.db._playable_characters,
quiet=True, search_object=True,
quiet=True,
)
) )
) )
@ -370,12 +372,14 @@ class CmdIC(COMMAND_DEFAULT_CLASS):
account.puppet_object(session, new_character) account.puppet_object(session, new_character)
account.db._last_puppet = new_character account.db._last_puppet = new_character
logger.log_sec( logger.log_sec(
f"Puppet Success: (Caller: {account}, Target: {new_character}, IP: {self.session.address})." f"Puppet Success: (Caller: {account}, Target: {new_character}, IP:"
f" {self.session.address})."
) )
except RuntimeError as exc: except RuntimeError as exc:
self.msg(f"|rYou cannot become |C{new_character.name}|n: {exc}") self.msg(f"|rYou cannot become |C{new_character.name}|n: {exc}")
logger.log_sec( logger.log_sec(
f"Puppet Failed: %s (Caller: {account}, Target: {new_character}, IP: {self.session.address})." f"Puppet Failed: %s (Caller: {account}, Target: {new_character}, IP:"
f" {self.session.address})."
) )
@ -670,7 +674,8 @@ class CmdOption(COMMAND_DEFAULT_CLASS):
else: else:
flags[new_name] = new_val flags[new_name] = new_val
self.msg( self.msg(
f"Option |w{new_name}|n was changed from '|w{old_val}|n' to '|w{new_val}|n'." f"Option |w{new_name}|n was changed from '|w{old_val}|n' to"
f" '|w{new_val}|n'."
) )
return {new_name: new_val} return {new_name: new_val}
except Exception as err: except Exception as err:
@ -1024,7 +1029,7 @@ class CmdStyle(COMMAND_DEFAULT_CLASS):
style <option> = <value> style <option> = <value>
Configure stylings for in-game display elements like table borders, help Configure stylings for in-game display elements like table borders, help
entriest etc. Use without arguments to see all available options. entries etc. Use without arguments to see all available options.
""" """

View file

@ -1925,6 +1925,7 @@ class CmdDiscord2Chan(COMMAND_DEFAULT_CLASS):
/delete - alias to remove /delete - alias to remove
/guild - toggle the Discord server tag on/off /guild - toggle the Discord server tag on/off
/channel - toggle the Evennia/Discord channel tags on/off /channel - toggle the Evennia/Discord channel tags on/off
/start - tell the bot to start, in case it lost its connection
Example: Example:
discord2chan mydiscord = 555555555555555 discord2chan mydiscord = 555555555555555
@ -1943,6 +1944,7 @@ class CmdDiscord2Chan(COMMAND_DEFAULT_CLASS):
"guild", "guild",
"list", "list",
"remove", "remove",
"start",
) )
locks = "cmd:serversetting(DISCORD_ENABLED) and pperm(Developer)" locks = "cmd:serversetting(DISCORD_ENABLED) and pperm(Developer)"
help_category = "Comms" help_category = "Comms"
@ -1973,6 +1975,13 @@ class CmdDiscord2Chan(COMMAND_DEFAULT_CLASS):
f"WARNING: The Discord bot's typeclass is '{discord_bot.typeclass_path}'. This does not match {settings.DISCORD_BOT_CLASS} in settings!" f"WARNING: The Discord bot's typeclass is '{discord_bot.typeclass_path}'. This does not match {settings.DISCORD_BOT_CLASS} in settings!"
) )
if "start" in self.switches:
if discord_bot.sessions.all():
self.msg("The Discord bot is already running.")
else:
discord_bot.start()
return
if "guild" in self.switches: if "guild" in self.switches:
discord_bot.db.tag_guild = not discord_bot.db.tag_guild discord_bot.db.tag_guild = not discord_bot.db.tag_guild
self.msg( self.msg(

View file

@ -371,8 +371,9 @@ class CmdInventory(COMMAND_DEFAULT_CLASS):
table = self.styled_table(border="header") table = self.styled_table(border="header")
for item in items: for item in items:
singular, _ = item.get_numbered_name(1, self.caller)
table.add_row( table.add_row(
f"|C{item.name}|n", f"|C{singular}|n",
"{}|n".format(utils.crop(raw_ansi(item.db.desc or ""), width=50) or ""), "{}|n".format(utils.crop(raw_ansi(item.db.desc or ""), width=50) or ""),
) )
string = f"|wYou are carrying:\n{table}" string = f"|wYou are carrying:\n{table}"
@ -424,8 +425,8 @@ class CmdGet(COMMAND_DEFAULT_CLASS):
if not success: if not success:
caller.msg("This can't be picked up.") caller.msg("This can't be picked up.")
else: else:
caller.msg(f"You pick up {obj.name}.") singular, _ = obj.get_numbered_name(1, caller)
caller.location.msg_contents(f"{caller.name} picks up {obj.name}.", exclude=caller) caller.location.msg_contents(f"$You() $conj(pick) up {singular}.", from_obj=caller)
# calling at_get hook method # calling at_get hook method
obj.at_get(caller) obj.at_get(caller)
@ -472,8 +473,8 @@ class CmdDrop(COMMAND_DEFAULT_CLASS):
if not success: if not success:
caller.msg("This couldn't be dropped.") caller.msg("This couldn't be dropped.")
else: else:
caller.msg("You drop %s." % (obj.name,)) singular, _ = obj.get_numbered_name(1, caller)
caller.location.msg_contents(f"{caller.name} drops {obj.name}.", exclude=caller) caller.location.msg_contents(f"$You() $conj(drop) {singular}.", from_obj=caller)
# Call the object script's at_drop() method. # Call the object script's at_drop() method.
obj.at_drop(caller) obj.at_drop(caller)
@ -510,11 +511,13 @@ class CmdGive(COMMAND_DEFAULT_CLASS):
target = caller.search(self.rhs) target = caller.search(self.rhs)
if not (to_give and target): if not (to_give and target):
return return
singular, _ = to_give.get_numbered_name(1, caller)
if target == caller: if target == caller:
caller.msg(f"You keep {to_give.key} to yourself.") caller.msg(f"You keep {singular} to yourself.")
return return
if not to_give.location == caller: if not to_give.location == caller:
caller.msg(f"You are not holding {to_give.key}.") caller.msg(f"You are not holding {singular}.")
return return
# calling at_pre_give hook method # calling at_pre_give hook method
@ -524,10 +527,10 @@ class CmdGive(COMMAND_DEFAULT_CLASS):
# give object # give object
success = to_give.move_to(target, quiet=True, move_type="give") success = to_give.move_to(target, quiet=True, move_type="give")
if not success: if not success:
caller.msg(f"You could not give {to_give.key}.") caller.msg(f"You could not give {singular} to {target.key}.")
else: else:
caller.msg(f"You give {to_give.key} to {target.key}.") caller.msg(f"You give {singular} to {target.key}.")
target.msg(f"{caller.key} gives you {to_give.key}.") target.msg(f"{caller.key} gives you {singular}.")
# Call the object script's at_give() method. # Call the object script's at_give() method.
to_give.at_give(caller, target) to_give.at_give(caller, target)

View file

@ -627,7 +627,7 @@ class CmdHelp(COMMAND_DEFAULT_CLASS):
) )
if suggestions: if suggestions:
help_text += ( help_text += (
"\n... But matches where found within the help " "\n... But matches were found within the help "
"texts of the suggestions below." "texts of the suggestions below."
) )
suggestions = [ suggestions = [

View file

@ -112,13 +112,13 @@ class TestGeneral(BaseEvenniaCommandTest):
self.call(general.CmdNick(), "/list", "Defined Nicks:") self.call(general.CmdNick(), "/list", "Defined Nicks:")
def test_get_and_drop(self): def test_get_and_drop(self):
self.call(general.CmdGet(), "Obj", "You pick up Obj.") self.call(general.CmdGet(), "Obj", "You pick up an Obj.")
self.call(general.CmdDrop(), "Obj", "You drop Obj.") self.call(general.CmdDrop(), "Obj", "You drop an Obj.")
def test_give(self): def test_give(self):
self.call(general.CmdGive(), "Obj to Char2", "You aren't carrying Obj.") self.call(general.CmdGive(), "Obj to Char2", "You aren't carrying Obj.")
self.call(general.CmdGive(), "Obj = Char2", "You aren't carrying Obj.") self.call(general.CmdGive(), "Obj = Char2", "You aren't carrying Obj.")
self.call(general.CmdGet(), "Obj", "You pick up Obj.") self.call(general.CmdGet(), "Obj", "You pick up an Obj.")
self.call(general.CmdGive(), "Obj to Char2", "You give") self.call(general.CmdGive(), "Obj to Char2", "You give")
self.call(general.CmdGive(), "Obj = Char", "You give", caller=self.char2) self.call(general.CmdGive(), "Obj = Char", "You give", caller=self.char2)

View file

@ -30,7 +30,7 @@ class Character(ComponentHolderMixin, DefaultCharacter):
Components need to inherit the Component class directly and require a name. Components need to inherit the Component class directly and require a name.
```python ```python
from evennia.contrib.components import Component from evennia.contrib.base_systems.components import Component
class Health(Component): class Health(Component):
name = "health" name = "health"

View file

@ -42,7 +42,7 @@ is being combined instead):
See the [sword example](evennia.contrib.game_systems.crafting.example_recipes) for an example See the [sword example](evennia.contrib.game_systems.crafting.example_recipes) for an example
of how to design a recipe tree for crafting a sword from base elements. of how to design a recipe tree for crafting a sword from base elements.
## Intallation and Usage ## Installation and Usage
Import the `CmdCraft` command from evennia/contrib/crafting/crafting.py and Import the `CmdCraft` command from evennia/contrib/crafting/crafting.py and
add it to your Character cmdset. Reload and the `craft` command will be add it to your Character cmdset. Reload and the `craft` command will be
@ -115,7 +115,7 @@ class RecipeBread(CraftingRecipe):
## Adding new recipes ## Adding new recipes
A *recipe* is a class inheriting from A *recipe* is a class inheriting from
`evennia.contrib.crafting.crafting.CraftingRecipe`. This class implements the `evennia.contrib.game_systems.crafting.CraftingRecipe`. This class implements the
most common form of crafting - that using in-game objects. Each recipe is a most common form of crafting - that using in-game objects. Each recipe is a
separate class which gets initialized with the consumables/tools you provide. separate class which gets initialized with the consumables/tools you provide.
@ -137,7 +137,7 @@ example setting:
```python ```python
# in mygame/world/myrecipes.py # in mygame/world/myrecipes.py
from evennia.contrib.crafting.crafting import CraftingRecipe from evennia.contrib.game_systems.crafting import CraftingRecipe
class WoodenPuppetRecipe(CraftingRecipe): class WoodenPuppetRecipe(CraftingRecipe):
"""A puppet"""" """A puppet""""
@ -200,7 +200,7 @@ in-game command:
In code we would do In code we would do
```python ```python
from evennia.contrib.crafting.crafting import craft from evennia.contrib.game_systems.crafting import craft
puppet = craft(crafter, "wooden puppet", knife, wood) puppet = craft(crafter, "wooden puppet", knife, wood)
``` ```
@ -259,7 +259,7 @@ parent class and have your recipes inherit from this.
```python ```python
from random import randint from random import randint
from evennia.contrib.crafting.crafting import CraftingRecipe from evennia.contrib.game_systems.crafting import CraftingRecipe
class SkillRecipe(CraftingRecipe): class SkillRecipe(CraftingRecipe):
"""A recipe that considers skill""" """A recipe that considers skill"""

View file

@ -16,11 +16,12 @@ In more detail, in `mygame/commands/default_cmdsets.py`:
```python ```python
... ...
from evennia.contrib import extended_room # <--- from evennia.contrib.grid import extended_room # <---
class CharacterCmdset(default_cmds.Character_CmdSet): class CharacterCmdset(default_cmds.CharacterCmdSet):
... ...
def at_cmdset_creation(self): def at_cmdset_creation(self):
super().at_cmdset_creation()
... ...
self.add(extended_room.ExtendedRoomCmdSet) # <--- self.add(extended_room.ExtendedRoomCmdSet) # <---
@ -28,7 +29,10 @@ class CharacterCmdset(default_cmds.Character_CmdSet):
Then reload to make the new commands available. Note that they only work Then reload to make the new commands available. Note that they only work
on rooms with the typeclass `ExtendedRoom`. Create new rooms with the right on rooms with the typeclass `ExtendedRoom`. Create new rooms with the right
typeclass or use the `typeclass` command to swap existing rooms. typeclass or use the `typeclass` command to swap existing rooms. Note that since
this contrib overrides the `look` command, you will need to add the
`extended_room.ExtendedRoomCmdSet` to the default character cmdset *after*
super().at_cmdset_creation(), or it will be overridden by the default look.
## Features ## Features

View file

@ -21,7 +21,7 @@ Specifically, in `mygame/commands/default_cmdsets.py`:
... ...
from evennia.contrib.grid.ingame_map_display import MapDisplayCmdSet # <--- from evennia.contrib.grid.ingame_map_display import MapDisplayCmdSet # <---
class CharacterCmdset(default_cmds.Character_CmdSet): class CharacterCmdset(default_cmds.CharacterCmdSet):
... ...
def at_cmdset_creation(self): def at_cmdset_creation(self):
... ...

View file

@ -2,14 +2,6 @@
XYZGrid - Griatch 2021 XYZGrid - Griatch 2021
""" """
from . import example, launchcmd, prototypes, tests, utils, xymap, xymap_legend, xyzgrid, xyzroom
from . import commands # noqa from . import commands # isort:skip - this needs to be imported last
from . import example # noqa
from . import launchcmd # noqa
from . import prototypes # noqa
from . import tests # noqa
from . import utils # noqa
from . import xymap # noqa
from . import xymap_legend # noqa
from . import xyzgrid # noqa
from . import xyzroom # noqa

View file

@ -463,7 +463,10 @@ class XYZRoom(DefaultRoom):
) )
sessions = looker.sessions.get() sessions = looker.sessions.get()
client_width, _ = sessions[0].get_client_size() if sessions else CLIENT_DEFAULT_WIDTH if sessions:
client_width, _ = sessions[0].get_client_size()
else:
client_width = CLIENT_DEFAULT_WIDTH
map_width = xymap.max_x map_width = xymap.max_x

View file

@ -166,7 +166,7 @@ This module adds no new commands; embed it in your say/emote/whisper commands.
### Usage: ### Usage:
```python ```python
from evennia.contrib import rplanguage from evennia.contrib.rpg.rpsystem import rplanguage
# need to be done once, here we create the "default" lang # need to be done once, here we create the "default" lang
rplanguage.add_language() rplanguage.add_language()

View file

@ -128,7 +128,6 @@ class EvAdventureDungeonRoom(EvAdventureRoom):
class EvAdventureDungeonExit(DefaultExit): class EvAdventureDungeonExit(DefaultExit):
""" """
Dungeon exit. This will not create the target room until it's traversed. Dungeon exit. This will not create the target room until it's traversed.
It must be created referencing the dungeon_orchestrator it belongs to.
""" """
@ -142,7 +141,8 @@ class EvAdventureDungeonExit(DefaultExit):
def at_traverse(self, traversing_object, target_location, **kwargs): def at_traverse(self, traversing_object, target_location, **kwargs):
""" """
Called when traversing. `target_location` will be None if the Called when traversing. `target_location` will be None if the
target was not yet created. target was not yet created. It checks the current location to get the
dungeon-orchestrator in use.
""" """
if target_location == self.location: if target_location == self.location:

View file

@ -7,7 +7,7 @@ object with its own functionality and state tracking.
Create the button with Create the button with
create/drop button:tutorials.red_button.RedButton create/drop button:contrib.tutorials.red_button.RedButton
Note that you must drop the button before you can see its messages! It's Note that you must drop the button before you can see its messages! It's
imperative that you press the red button. You know you want to. imperative that you press the red button. You know you want to.

View file

@ -17,7 +17,7 @@ Evmenu.
Log in as superuser (#1), then run Log in as superuser (#1), then run
batchcommand tutorials.tutorial_world.build batchcommand contrib.tutorials.tutorial_world.build
Wait a little while for building to complete and don't run the command Wait a little while for building to complete and don't run the command
again even if it's slow. This builds the world and connect it to Limbo again even if it's slow. This builds the world and connect it to Limbo

View file

@ -31,25 +31,27 @@ HELP_ENTRY_DICTS = [
"category": "General", "category": "General",
"locks": "read:perm(Developer)", "locks": "read:perm(Developer)",
"text": """ "text": """
Evennia is a MUD game server in Python. Evennia is a MU-game server and framework written in Python. You can read more
on https://www.evennia.com.
# subtopics # subtopics
## Installation ## Installation
You'll find installation instructions on https:evennia.com You'll find installation instructions on https://www.evennia.com.
## Community ## Community
There are many ways to get help and communicate with other devs! There are many ways to get help and communicate with other devs!
### IRC ### Discussions
The irc channel is #evennia on irc.freenode.net The Discussions forum is found at https://github.com/evennia/evennia/discussions.
### Discord ### Discord
There is also a discord channel you can find from the sidebard on evennia.com. There is also a discord channel for chatting - connect using the
following link: https://discord.gg/AJJpcRUhtF
""", """,
}, },
@ -58,7 +60,7 @@ HELP_ENTRY_DICTS = [
"category": "building", "category": "building",
"text": """ "text": """
Evennia comes with a bunch of default building commands. You can Evennia comes with a bunch of default building commands. You can
find a building tutorial in the evennia documentation. find a beginner tutorial in the Evennia documentation.
""", """,
}, },

View file

@ -8,7 +8,7 @@ msgstr ""
"Project-Id-Version: \n" "Project-Id-Version: \n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2022-10-29 18:53+0000\n" "POT-Creation-Date: 2022-10-29 18:53+0000\n"
"PO-Revision-Date: 2022-03-20 19:55+0100\n" "PO-Revision-Date: 2022-12-16 15:09+0100\n"
"Last-Translator: Christophe Petry <toktoktheeo@outlook.com>\n" "Last-Translator: Christophe Petry <toktoktheeo@outlook.com>\n"
"Language-Team: \n" "Language-Team: \n"
"Language: fr\n" "Language: fr\n"
@ -16,7 +16,7 @@ msgstr ""
"Content-Type: text/plain; charset=UTF-8\n" "Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n" "Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n > 1);\n" "Plural-Forms: nplurals=2; plural=(n > 1);\n"
"X-Generator: Poedit 3.0.1\n" "X-Generator: Poedit 3.2.2\n"
#: accounts/accounts.py:341 #: accounts/accounts.py:341
#, python-brace-format #, python-brace-format
@ -28,6 +28,8 @@ msgstr "|c{key}|R est déjà contrôlé par un autre compte."
msgid "" msgid ""
"You cannot control any more puppets (max {_MAX_NR_SIMULTANEOUS_PUPPETS})" "You cannot control any more puppets (max {_MAX_NR_SIMULTANEOUS_PUPPETS})"
msgstr "" msgstr ""
"Vous ne pouvez contrôler plus de poupées (Maximum : "
"{_MAX_NR_SIMULTANEOUS_PUPPETS})"
#: accounts/accounts.py:555 #: accounts/accounts.py:555
msgid "Too many login failures; please try again in a few minutes." msgid "Too many login failures; please try again in a few minutes."
@ -73,9 +75,8 @@ msgstr ""
"problème persiste." "problème persiste."
#: accounts/accounts.py:918 #: accounts/accounts.py:918
#, fuzzy
msgid "Account being deleted." msgid "Account being deleted."
msgstr "Suppression du compte." msgstr "Le compte a été supprimé."
#: accounts/accounts.py:1475 accounts/accounts.py:1819 #: accounts/accounts.py:1475 accounts/accounts.py:1819
#, python-brace-format #, python-brace-format
@ -382,12 +383,12 @@ msgstr "Vous avez maintenant {name} en votre possession."
#: objects/objects.py:1863 #: objects/objects.py:1863
#, python-brace-format #, python-brace-format
msgid "{object} arrives to {destination} from {origin}." msgid "{object} arrives to {destination} from {origin}."
msgstr "" msgstr "{object} arrive à {destination} depuis {origin}."
#: objects/objects.py:1865 #: objects/objects.py:1865
#, python-brace-format #, python-brace-format
msgid "{object} arrives to {destination}." msgid "{object} arrives to {destination}."
msgstr "" msgstr "{object} arrive à {destination}."
#: objects/objects.py:2530 #: objects/objects.py:2530
msgid "Invalid character name." msgid "Invalid character name."
@ -421,10 +422,9 @@ msgid "{name} has entered the game."
msgstr "{name} est entré(e) dans le jeu." msgstr "{name} est entré(e) dans le jeu."
#: objects/objects.py:2716 #: objects/objects.py:2716
#, fuzzy, python-brace-format #, python-brace-format
#| msgid "{name} has left the game."
msgid "{name} has left the game{reason}." msgid "{name} has left the game{reason}."
msgstr "{name} est sorti(e) du jeu." msgstr "{name} est sorti(e) du jeu ({reason})."
#: objects/objects.py:2838 #: objects/objects.py:2838
msgid "This is a room." msgid "This is a room."
@ -552,6 +552,8 @@ msgid ""
"Diff contains non-dicts that are not on the form (old, new, action_to_take): " "Diff contains non-dicts that are not on the form (old, new, action_to_take): "
"{diffpart}" "{diffpart}"
msgstr "" msgstr ""
"\"diff\" contient des non-dicts qui ne sont pas formatés (ancien, nouveau, "
"action à prendre): {diffpart}"
#: scripts/scripthandler.py:51 #: scripts/scripthandler.py:51
#, fuzzy, python-brace-format #, fuzzy, python-brace-format
@ -613,7 +615,7 @@ msgstr "délai d'inactivité dépassé"
#: server/server.py:177 #: server/server.py:177
msgid " (connection lost)" msgid " (connection lost)"
msgstr "" msgstr " (connexion perdue)"
#: server/sessionhandler.py:41 #: server/sessionhandler.py:41
msgid "Your client sent an incorrect UTF-8 sequence." msgid "Your client sent an incorrect UTF-8 sequence."
@ -738,6 +740,8 @@ msgstr ""
#: utils/eveditor.py:143 #: utils/eveditor.py:143
msgid "|rNo save function defined. Buffer cannot be saved.|n" msgid "|rNo save function defined. Buffer cannot be saved.|n"
msgstr "" msgstr ""
"|rAucune fonction d'enregistrement définie. La pile ne peut être "
"sauvegardée.|n"
#: utils/eveditor.py:145 #: utils/eveditor.py:145
msgid "No changes need saving" msgid "No changes need saving"
@ -745,7 +749,7 @@ msgstr "Aucune modification ne doit être sauvegardée"
#: utils/eveditor.py:146 #: utils/eveditor.py:146
msgid "Exited editor." msgid "Exited editor."
msgstr "Sortie de l'éditeur:" msgstr "Sortie de l'éditeur."
#: utils/eveditor.py:149 #: utils/eveditor.py:149
#, python-brace-format #, python-brace-format
@ -755,6 +759,10 @@ msgid ""
"\n" "\n"
"|rQuit function gave an error. Skipping.|n\n" "|rQuit function gave an error. Skipping.|n\n"
msgstr "" msgstr ""
"\n"
"{error}\n"
"\n"
"|rLa fonction quitter à retourner une erreur. On passe.|n\n"
#: utils/eveditor.py:157 #: utils/eveditor.py:157
#, python-brace-format #, python-brace-format
@ -766,6 +774,13 @@ msgid ""
"to non-persistent mode (which means the editor session won't survive\n" "to non-persistent mode (which means the editor session won't survive\n"
"an eventual server reload - so save often!)|n\n" "an eventual server reload - so save often!)|n\n"
msgstr "" msgstr ""
"\n"
"{error}\n"
"\n"
"L'éditeur n'a pas pu sauvergarder en mode persistent. Changement pour le "
"mode non-persistant. \n"
"Cela signifie que l'éditeur ne survivra pas à un rechargement du serveur, \n"
"alors sauvegardez souvent !\n"
#: utils/eveditor.py:167 #: utils/eveditor.py:167
msgid "" msgid ""
@ -773,26 +788,29 @@ msgid ""
"EvEditor callbacks could not be pickled, for example because it's a class " "EvEditor callbacks could not be pickled, for example because it's a class "
"method or is defined inside another function." "method or is defined inside another function."
msgstr "" msgstr ""
"EvEditeur erreur du mode persistant. Usuellement, c'est lorsque un ou "
"plusieurs appel n'ont pu aboutir dans l'éditeur. Par exemple, c'est "
"parcequ'une méthode de class et définie à l'intérieur d'une autre fonction."
#: utils/eveditor.py:173 #: utils/eveditor.py:173
msgid "Nothing to undo." msgid "Nothing to undo."
msgstr "" msgstr "Rien pour revenir en arrière."
#: utils/eveditor.py:174 #: utils/eveditor.py:174
msgid "Nothing to redo." msgid "Nothing to redo."
msgstr "" msgstr "Rien à rétablir."
#: utils/eveditor.py:175 #: utils/eveditor.py:175
msgid "Undid one step." msgid "Undid one step."
msgstr "" msgstr "Un pas supprimé."
#: utils/eveditor.py:176 #: utils/eveditor.py:176
msgid "Redid one step." msgid "Redid one step."
msgstr "" msgstr "Un pas ajouté."
#: utils/eveditor.py:494 #: utils/eveditor.py:494
msgid "Single ':' added to buffer." msgid "Single ':' added to buffer."
msgstr "" msgstr "Un seul ':' ajouté à la pile."
#: utils/eveditor.py:509 #: utils/eveditor.py:509
msgid "Save before quitting?" msgid "Save before quitting?"
@ -806,7 +824,7 @@ msgstr ""
#: utils/eveditor.py:529 #: utils/eveditor.py:529
#, python-brace-format #, python-brace-format
msgid "Deleted {string}." msgid "Deleted {string}."
msgstr "" msgstr "{string} supprimé."
#: utils/eveditor.py:534 #: utils/eveditor.py:534
msgid "You must give a search word to delete." msgid "You must give a search word to delete."
@ -815,17 +833,17 @@ msgstr "Vous devez donner un mot de recherche à supprimer."
#: utils/eveditor.py:540 #: utils/eveditor.py:540
#, python-brace-format #, python-brace-format
msgid "Removed {arg1} for lines {l1}-{l2}." msgid "Removed {arg1} for lines {l1}-{l2}."
msgstr "" msgstr "{arg1} retiré des lignes {l1}-{l2}."
#: utils/eveditor.py:546 #: utils/eveditor.py:546
#, python-brace-format #, python-brace-format
msgid "Removed {arg1} for {line}." msgid "Removed {arg1} for {line}."
msgstr "" msgstr "{arg1} retiré de la ligne {line}"
#: utils/eveditor.py:562 #: utils/eveditor.py:562
#, python-brace-format #, python-brace-format
msgid "Cleared {nlines} lines from buffer." msgid "Cleared {nlines} lines from buffer."
msgstr "" msgstr "{nlines} lignes nettoyées depuis la pile."
#: utils/eveditor.py:567 #: utils/eveditor.py:567
#, python-brace-format #, python-brace-format
@ -835,7 +853,7 @@ msgstr ""
#: utils/eveditor.py:574 #: utils/eveditor.py:574
#, python-brace-format #, python-brace-format
msgid "{line}, {cbuf} cut." msgid "{line}, {cbuf} cut."
msgstr "" msgstr "{line}, {cbuf} coupée."
#: utils/eveditor.py:578 #: utils/eveditor.py:578
msgid "Copy buffer is empty." msgid "Copy buffer is empty."
@ -844,7 +862,7 @@ msgstr "Le tampon de copie est vide."
#: utils/eveditor.py:583 #: utils/eveditor.py:583
#, python-brace-format #, python-brace-format
msgid "Pasted buffer {cbuf} to {line}." msgid "Pasted buffer {cbuf} to {line}."
msgstr "" msgstr "La Pile (buffer) copié de {cbuf} à {line}"
#: utils/eveditor.py:591 #: utils/eveditor.py:591
msgid "You need to enter a new line and where to insert it." msgid "You need to enter a new line and where to insert it."
@ -853,7 +871,7 @@ msgstr "Vous devez saisir une nouvelle ligne et indiquer où l'insérer."
#: utils/eveditor.py:596 #: utils/eveditor.py:596
#, python-brace-format #, python-brace-format
msgid "Inserted {num} new line(s) at {line}." msgid "Inserted {num} new line(s) at {line}."
msgstr "" msgstr "{num} ligne(s) insérée(s) depuis la ligne : {line}."
#: utils/eveditor.py:604 #: utils/eveditor.py:604
msgid "You need to enter a replacement string." msgid "You need to enter a replacement string."
@ -862,7 +880,7 @@ msgstr "Vous devez saisir une chaîne de remplacement."
#: utils/eveditor.py:609 #: utils/eveditor.py:609
#, python-brace-format #, python-brace-format
msgid "Replaced {num} line(s) at {line}." msgid "Replaced {num} line(s) at {line}."
msgstr "" msgstr "{num} lignes remplacées à partir de la ligne {line}."
#: utils/eveditor.py:616 #: utils/eveditor.py:616
msgid "You need to enter text to insert." msgid "You need to enter text to insert."
@ -871,16 +889,16 @@ msgstr "Vous devez saisir le texte à insérer."
#: utils/eveditor.py:624 #: utils/eveditor.py:624
#, python-brace-format #, python-brace-format
msgid "Inserted text at beginning of {line}." msgid "Inserted text at beginning of {line}."
msgstr "" msgstr "Texte ajouté au début de la ligne {line}."
#: utils/eveditor.py:628 #: utils/eveditor.py:628
msgid "You need to enter text to append." msgid "You need to enter text to append."
msgstr "" msgstr "Vous avez besoin d'insérer du texte à ajouter."
#: utils/eveditor.py:636 #: utils/eveditor.py:636
#, python-brace-format #, python-brace-format
msgid "Appended text to end of {line}." msgid "Appended text to end of {line}."
msgstr "" msgstr "Text ajouté à la fin de la ligne {line}."
#: utils/eveditor.py:641 #: utils/eveditor.py:641
msgid "You must give a search word and something to replace it with." msgid "You must give a search word and something to replace it with."
@ -890,36 +908,36 @@ msgstr ""
#: utils/eveditor.py:647 #: utils/eveditor.py:647
#, python-brace-format #, python-brace-format
msgid "Search-replaced {arg1} -> {arg2} for lines {l1}-{l2}." msgid "Search-replaced {arg1} -> {arg2} for lines {l1}-{l2}."
msgstr "" msgstr "Rechercher-remplacer {arg1} -> {arg2} pour les lignes {l1}-{l2}."
#: utils/eveditor.py:653 #: utils/eveditor.py:653
#, python-brace-format #, python-brace-format
msgid "Search-replaced {arg1} -> {arg2} for {line}." msgid "Search-replaced {arg1} -> {arg2} for {line}."
msgstr "" msgstr "Recherche-remplacer {arg1} -> {arg2} pour la ligne {line}."
#: utils/eveditor.py:677 #: utils/eveditor.py:677
#, python-brace-format #, python-brace-format
msgid "Flood filled lines {l1}-{l2}." msgid "Flood filled lines {l1}-{l2}."
msgstr "" msgstr "Lignes remplies (inondées ?) {l1}-{l2}."
#: utils/eveditor.py:679 #: utils/eveditor.py:679
#, python-brace-format #, python-brace-format
msgid "Flood filled {line}." msgid "Flood filled {line}."
msgstr "" msgstr "\"flood\" rempli {line}."
#: utils/eveditor.py:701 #: utils/eveditor.py:701
msgid "Valid justifications are" msgid "Valid justifications are"
msgstr "" msgstr "Les justification validées sont"
#: utils/eveditor.py:710 #: utils/eveditor.py:710
#, python-brace-format #, python-brace-format
msgid "{align}-justified lines {l1}-{l2}." msgid "{align}-justified lines {l1}-{l2}."
msgstr "" msgstr "{align}-lignes justifiées{l1}-{l2}."
#: utils/eveditor.py:716 #: utils/eveditor.py:716
#, python-brace-format #, python-brace-format
msgid "{align}-justified {line}." msgid "{align}-justified {line}."
msgstr "" msgstr "{align}-justified {line}."
#: utils/eveditor.py:728 #: utils/eveditor.py:728
#, python-brace-format #, python-brace-format
@ -976,11 +994,11 @@ msgstr "Auto-indentation désactivée."
#: utils/eveditor.py:1093 #: utils/eveditor.py:1093
#, python-brace-format #, python-brace-format
msgid "Line Editor [{name}]" msgid "Line Editor [{name}]"
msgstr "" msgstr "Édition ligne [{name}]"
#: utils/eveditor.py:1101 #: utils/eveditor.py:1101
msgid "(:h for help)" msgid "(:h for help)"
msgstr "" msgstr "(:h pour l'aide)"
#: utils/evmenu.py:302 #: utils/evmenu.py:302
#, python-brace-format #, python-brace-format
@ -1026,15 +1044,15 @@ msgstr "|rChoix invalide.|n"
#: utils/evmenu.py:1439 #: utils/evmenu.py:1439
msgid "|Wcurrent|n" msgid "|Wcurrent|n"
msgstr "" msgstr "|WActuel|n"
#: utils/evmenu.py:1447 #: utils/evmenu.py:1447
msgid "|wp|Wrevious page|n" msgid "|wp|Wrevious page|n"
msgstr "" msgstr "|wp|Wage précédente|n"
#: utils/evmenu.py:1454 #: utils/evmenu.py:1454
msgid "|wn|Wext page|n" msgid "|wn|Wext page|n"
msgstr "" msgstr "|wp|Wpage suivante|n"
#: utils/evmenu.py:1689 #: utils/evmenu.py:1689
msgid "Aborted." msgid "Aborted."

View file

@ -538,8 +538,16 @@ def _get_twistd_cmdline(pprofiler, sprofiler):
Compile the command line for starting a Twisted application using the 'twistd' executable. Compile the command line for starting a Twisted application using the 'twistd' executable.
""" """
portal_cmd = [TWISTED_BINARY, "--python={}".format(PORTAL_PY_FILE)] portal_cmd = [
server_cmd = [TWISTED_BINARY, "--python={}".format(SERVER_PY_FILE)] TWISTED_BINARY,
f"--python={PORTAL_PY_FILE}",
"--logger=evennia.utils.logger.GetPortalLogObserver",
]
server_cmd = [
TWISTED_BINARY,
f"--python={SERVER_PY_FILE}",
"--logger=evennia.utils.logger.GetServerLogObserver",
]
if os.name != "nt": if os.name != "nt":
# PID files only for UNIX # PID files only for UNIX
@ -1363,8 +1371,8 @@ def set_gamedir(path):
global GAMEDIR global GAMEDIR
Ndepth = 10 Ndepth = 10
settings_path = os.path.join("server", "conf", "settings.py") settings_path = SETTINGS_DOTPATH.replace(".", os.sep) + ".py"
os.chdir(GAMEDIR) os.chdir(path)
for i in range(Ndepth): for i in range(Ndepth):
gpath = os.getcwd() gpath = os.getcwd()
if "server" in os.listdir(gpath): if "server" in os.listdir(gpath):

View file

@ -84,7 +84,7 @@ def should_retry(status_code):
class DiscordWebsocketServerFactory(WebSocketClientFactory, protocol.ReconnectingClientFactory): class DiscordWebsocketServerFactory(WebSocketClientFactory, protocol.ReconnectingClientFactory):
""" """
A variant of the websocket-factory that auto-reconnects. A customized websocket client factory that navigates the Discord gateway process.
""" """
@ -94,7 +94,7 @@ class DiscordWebsocketServerFactory(WebSocketClientFactory, protocol.Reconnectin
noisy = False noisy = False
gateway = None gateway = None
resume_url = None resume_url = None
do_retry = True is_connecting = False
def __init__(self, sessionhandler, *args, **kwargs): def __init__(self, sessionhandler, *args, **kwargs):
self.uid = kwargs.get("uid") self.uid = kwargs.get("uid")
@ -122,8 +122,8 @@ class DiscordWebsocketServerFactory(WebSocketClientFactory, protocol.Reconnectin
d = readBody(response) d = readBody(response)
d.addCallback(self.websocket_init, *args, **kwargs) d.addCallback(self.websocket_init, *args, **kwargs)
return d return d
elif should_retry(response.code): else:
delay(300, self.get_gateway_url, *args, **kwargs) logger.log_warn("Discord gateway request failed.")
d.addCallback(cbResponse) d.addCallback(cbResponse)
@ -132,6 +132,7 @@ class DiscordWebsocketServerFactory(WebSocketClientFactory, protocol.Reconnectin
callback for when the URL is gotten callback for when the URL is gotten
""" """
data = json.loads(str(payload, "utf-8")) data = json.loads(str(payload, "utf-8"))
self.is_connecting = False
if url := data.get("url"): if url := data.get("url"):
self.gateway = f"{url}/?v={DISCORD_API_VERSION}&encoding=json".encode("utf-8") self.gateway = f"{url}/?v={DISCORD_API_VERSION}&encoding=json".encode("utf-8")
useragent = kwargs.pop("useragent", DISCORD_USER_AGENT) useragent = kwargs.pop("useragent", DISCORD_USER_AGENT)
@ -179,30 +180,7 @@ class DiscordWebsocketServerFactory(WebSocketClientFactory, protocol.Reconnectin
connector (Connector): Represents the connection. connector (Connector): Represents the connection.
""" """
logger.log_info("Attempting connection to Discord...") logger.log_info("Connecting to Discord...")
def clientConnectionFailed(self, connector, reason):
"""
Called when Client failed to connect.
Args:
connector (Connection): Represents the connection.
reason (str): The reason for the failure.
"""
protocol.ReconnectingClientFactory.clientConnectionLost(self, connector, reason)
def clientConnectionLost(self, connector, reason):
"""
Called when Client loses connection.
Args:
connector (Connection): Represents the connection.
reason (str): The reason for the failure.
"""
if self.do_retry or not self.bot:
self.retry(connector)
def reconnect(self): def reconnect(self):
""" """
@ -210,33 +188,30 @@ class DiscordWebsocketServerFactory(WebSocketClientFactory, protocol.Reconnectin
de-registering the session and then reattaching a new one. de-registering the session and then reattaching a new one.
""" """
# set the retry flag to False so it doesn't attempt an automatic retry
# and duplicate the connection
self.do_retry = False
# disconnect everything
self.bot.transport.loseConnection()
self.sessionhandler.server_disconnect(self.bot)
# set up the reconnection # set up the reconnection
if self.resume_url: if self.resume_url:
self.url = self.resume_url self.url = self.resume_url
elif self.gateway: elif self.gateway:
self.url = self.gateway self.url = self.gateway
else: else:
# we don't know where to reconnect to! start from the beginning # we don't know where to reconnect to! we'll start from the beginning
self.get_gateway_url() self.url = None
return # reset the internal delay, since this is a deliberate disconnect
self.start() self.delay = self.initialDelay
# disconnect to allow the reconnection process to kick in
self.bot.sendClose()
self.sessionhandler.server_disconnect(self.bot)
def start(self): def start(self):
"Connect protocol to remote server" "Connect protocol to remote server"
if not self.gateway: if not self.gateway:
# we can't actually start yet # we don't know where to connect to
# get the gateway URL from Discord # get the gateway URL from Discord
self.is_connecting = True
self.get_gateway_url() self.get_gateway_url()
else: elif not self.is_connecting:
# set the retry flag so we maintain this connection # everything is good, connect
self.do_retry = True
connectWS(self) connectWS(self)
@ -255,7 +230,6 @@ class DiscordClient(WebSocketClientProtocol, _BASE_SESSION_CLASS):
def __init__(self): def __init__(self):
WebSocketClientProtocol.__init__(self) WebSocketClientProtocol.__init__(self)
_BASE_SESSION_CLASS.__init__(self) _BASE_SESSION_CLASS.__init__(self)
self.restart_downtime = None
def at_login(self): def at_login(self):
pass pass
@ -265,8 +239,7 @@ class DiscordClient(WebSocketClientProtocol, _BASE_SESSION_CLASS):
Called when connection is established. Called when connection is established.
""" """
self.restart_downtime = None logger.log_msg("Discord connection established.")
self.restart_task = None
self.factory.bot = self self.factory.bot = self
self.init_session("discord", "discord.gg", self.factory.sessionhandler) self.init_session("discord", "discord.gg", self.factory.sessionhandler)
@ -352,11 +325,11 @@ class DiscordClient(WebSocketClientProtocol, _BASE_SESSION_CLASS):
""" """
if self.nextHeartbeatCall: if self.nextHeartbeatCall:
self.nextHeartbeatCall.cancel() self.nextHeartbeatCall.cancel()
self.disconnect(reason) self.nextHeartbeatCall = None
if code >= 4000: if wasClean:
logger.log_err(f"Discord connection closed: {reason}") logger.log_info(f"Discord connection closed ({code}) reason: {reason}")
else: else:
logger.log_info(f"Discord disconnected: {reason}") logger.log_info(f"Discord connection lost.")
def _send_json(self, data): def _send_json(self, data):
""" """

View file

@ -68,27 +68,27 @@ class TestLauncher(TwistedTestCase):
@patch("evennia.server.evennia_launcher.os.name", new="posix") @patch("evennia.server.evennia_launcher.os.name", new="posix")
def test_get_twisted_cmdline(self): def test_get_twisted_cmdline(self):
pcmd, scmd = evennia_launcher._get_twistd_cmdline(False, False) pcmd, scmd = evennia_launcher._get_twistd_cmdline(False, False)
self.assertTrue("portal.py" in pcmd[1]) self.assertIn("portal.py", pcmd[1])
self.assertTrue("--pidfile" in pcmd[2]) self.assertIn("--pidfile", pcmd[3])
self.assertTrue("server.py" in scmd[1]) self.assertIn("server.py", scmd[1])
self.assertTrue("--pidfile" in scmd[2]) self.assertIn("--pidfile", scmd[3])
pcmd, scmd = evennia_launcher._get_twistd_cmdline(True, True) pcmd, scmd = evennia_launcher._get_twistd_cmdline(True, True)
self.assertTrue("portal.py" in pcmd[1]) self.assertIn("portal.py", pcmd[1])
self.assertTrue("--pidfile" in pcmd[2]) self.assertIn("--pidfile", pcmd[3])
self.assertTrue("--profiler=cprofile" in pcmd[4], "actual: {}".format(pcmd)) self.assertIn("--profiler=cprofile", pcmd[5], pcmd)
self.assertTrue("--profile=" in pcmd[5]) self.assertIn("--profile=", pcmd[6])
self.assertTrue("server.py" in scmd[1]) self.assertIn("server.py", scmd[1])
self.assertTrue("--pidfile" in scmd[2]) self.assertIn("--pidfile", scmd[3])
self.assertTrue("--pidfile" in scmd[2]) self.assertIn("--pidfile", scmd[3])
self.assertTrue("--profiler=cprofile" in scmd[4], "actual: {}".format(scmd)) self.assertIn("--profiler=cprofile", scmd[5], "actual: {}".format(scmd))
self.assertTrue("--profile=" in scmd[5]) self.assertIn("--profile=", scmd[6])
@patch("evennia.server.evennia_launcher.os.name", new="nt") @patch("evennia.server.evennia_launcher.os.name", new="nt")
def test_get_twisted_cmdline_nt(self): def test_get_twisted_cmdline_nt(self):
pcmd, scmd = evennia_launcher._get_twistd_cmdline(False, False) pcmd, scmd = evennia_launcher._get_twistd_cmdline(False, False)
self.assertTrue(len(pcmd) == 2, "actual: {}".format(pcmd)) self.assertTrue(len(pcmd) == 3, pcmd)
self.assertTrue(len(scmd) == 2, "actual: {}".format(scmd)) self.assertTrue(len(scmd) == 3, scmd)
@patch("evennia.server.evennia_launcher.reactor.stop") @patch("evennia.server.evennia_launcher.reactor.stop")
def test_reactor_stop(self, mockstop): def test_reactor_stop(self, mockstop):

View file

@ -1737,7 +1737,7 @@ class NickHandler(AttributeHandler):
regex = re.compile(nick_regex, re.I + re.DOTALL + re.U) regex = re.compile(nick_regex, re.I + re.DOTALL + re.U)
self._regex_cache[nick_regex] = regex self._regex_cache[nick_regex] = regex
is_match, raw_string = parse_nick_template(raw_string.strip(), regex, template) is_match, raw_string = parse_nick_template(raw_string, regex, template)
if is_match: if is_match:
break break
return raw_string return raw_string

View file

@ -30,6 +30,7 @@ except ImportError:
from django.contrib.contenttypes.models import ContentType from django.contrib.contenttypes.models import ContentType
from django.core.exceptions import ObjectDoesNotExist from django.core.exceptions import ObjectDoesNotExist
from django.utils.safestring import SafeString from django.utils.safestring import SafeString
from evennia.utils import logger from evennia.utils import logger
from evennia.utils.utils import is_iter, to_bytes, uses_database from evennia.utils.utils import is_iter, to_bytes, uses_database

View file

@ -273,22 +273,12 @@ from django.conf import settings
# i18n # i18n
from django.utils.translation import gettext as _ from django.utils.translation import gettext as _
from evennia import CmdSet, Command from evennia import CmdSet, Command
from evennia.commands import cmdhandler from evennia.commands import cmdhandler
from evennia.utils import logger from evennia.utils import logger
from evennia.utils.ansi import strip_ansi from evennia.utils.ansi import strip_ansi
from evennia.utils.evtable import EvColumn, EvTable from evennia.utils.evtable import EvColumn, EvTable
from evennia.utils.utils import ( from evennia.utils.utils import crop, dedent, is_iter, m_len, make_iter, mod_import, pad, to_str
crop,
dedent,
is_iter,
m_len,
make_iter,
mod_import,
pad,
to_str,
)
# read from protocol NAWS later? # read from protocol NAWS later?
_MAX_TEXT_WIDTH = settings.CLIENT_DEFAULT_WIDTH _MAX_TEXT_WIDTH = settings.CLIENT_DEFAULT_WIDTH

View file

@ -288,8 +288,8 @@ def justify(text, width=None, align="l", indent=0, fillchar=" "):
# absolute mode - just crop or fill to width # absolute mode - just crop or fill to width
abs_lines = [] abs_lines = []
for line in text.split("\n"): for line in text.split("\n"):
nlen = len(line) nlen = m_len(line)
if len(line) < width: if m_len(line) < width:
line += sp * (width - nlen) line += sp * (width - nlen)
else: else:
line = crop(line, width=width, suffix="") line = crop(line, width=width, suffix="")
@ -304,7 +304,7 @@ def justify(text, width=None, align="l", indent=0, fillchar=" "):
for ip, paragraph in enumerate(paragraphs): for ip, paragraph in enumerate(paragraphs):
if ip > 0: if ip > 0:
words.append(("\n", 0)) words.append(("\n", 0))
words.extend((word, len(word)) for word in paragraph.split()) words.extend((word, m_len(word)) for word in paragraph.split())
if not words: if not words:
# Just whitespace! # Just whitespace!

View file

@ -4,98 +4,117 @@ build-backend = "setuptools.build_meta"
[project] [project]
name = "evennia" name = "evennia"
version = "1.0rc11" version = "1.0.2"
maintainers = [ maintainers = [{ name = "Griatch", email = "griatch@gmail.com" }]
{ name="Griatch", email="griatch@gmail.com" },
]
description = "A full-featured toolkit and server for text-based multiplayer games (MUDs, MU*, etc)." description = "A full-featured toolkit and server for text-based multiplayer games (MUDs, MU*, etc)."
requires-python = ">=3.10" requires-python = ">=3.10"
readme = { file="README.md", content-type="text/markdown" } readme = { file = "README.md", content-type = "text/markdown" }
license = { text="BSD" } license = { text = "BSD" }
keywords = [ keywords = [
"MUD", "MUSH", "MUX", "MMO", "text-only", "multiplayer", "online", "rpg", "game", "engine", "MUD",
"framework", "text", "adventure", "telnet", "websocket", "blind", "accessible", "ascii", "MUSH",
"utf-8", "terminal", "online", "server", "beginner", "tutorials" "MUX",
"MMO",
"text-only",
"multiplayer",
"online",
"rpg",
"game",
"engine",
"framework",
"text",
"adventure",
"telnet",
"websocket",
"blind",
"accessible",
"ascii",
"utf-8",
"terminal",
"online",
"server",
"beginner",
"tutorials",
] ]
classifiers = [ classifiers = [
"Programming Language :: Python :: 3 :: Only", "Programming Language :: Python :: 3 :: Only",
"Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.11",
"Programming Language :: JavaScript", "Programming Language :: JavaScript",
"Development Status :: 5 - Production/Stable", "Development Status :: 5 - Production/Stable",
"License :: OSI Approved :: BSD License", "License :: OSI Approved :: BSD License",
"Environment :: Console", "Environment :: Console",
"Environment :: Web Environment", "Environment :: Web Environment",
"Framework :: Django", "Framework :: Django",
"Framework :: Twisted", "Framework :: Twisted",
"Intended Audience :: Developers", "Intended Audience :: Developers",
"Intended Audience :: Education", "Intended Audience :: Education",
"Operating System :: MacOS", "Operating System :: MacOS",
"Operating System :: Microsoft :: Windows", "Operating System :: Microsoft :: Windows",
"Operating System :: POSIX :: Linux", "Operating System :: POSIX :: Linux",
"Topic :: Database", "Topic :: Database",
"Topic :: Education", "Topic :: Education",
"Topic :: Games/Entertainment :: Multi-User Dungeons (MUD)", "Topic :: Games/Entertainment :: Multi-User Dungeons (MUD)",
"Topic :: Games/Entertainment :: Puzzle Games", "Topic :: Games/Entertainment :: Puzzle Games",
"Topic :: Games/Entertainment :: Role-Playing", "Topic :: Games/Entertainment :: Role-Playing",
"Topic :: Games/Entertainment :: Simulation", "Topic :: Games/Entertainment :: Simulation",
"Topic :: Software Development :: Libraries :: Application Frameworks", "Topic :: Software Development :: Libraries :: Application Frameworks",
"Topic :: Internet :: WWW/HTTP :: WSGI :: Server" "Topic :: Internet :: WWW/HTTP :: WSGI :: Server",
] ]
dependencies = [ dependencies = [
# core dependencies # core dependencies
"django >= 4.1.3, < 4.2", "django >= 4.1.3, < 4.2",
"twisted >= 22.10, < 23", "twisted >= 22.10, < 23",
"pytz >= 2022.6", "pytz >= 2022.6",
"djangorestframework >= 3.14, < 3.15", "djangorestframework >= 3.14, < 3.15",
"pyyaml >= 6.0", "pyyaml >= 6.0",
"django-filter == 2.4", "django-filter == 2.4",
"django-sekizai == 2.0.0", "django-sekizai == 2.0.0",
"inflect >= 5.2.0", "inflect >= 5.2.0",
"autobahn >= 20.7.1, < 21.0.0", "autobahn >= 20.7.1, < 21.0.0",
"lunr == 0.6.0", "lunr == 0.6.0",
"simpleeval <= 1.0", "simpleeval <= 1.0",
"uritemplate == 4.1.1", "uritemplate == 4.1.1",
"Jinja2 < 3.1", "Jinja2 < 3.1",
"tzdata >= 2022.6", "tzdata >= 2022.6",
# for unit tests and code formatting # for unit tests and code formatting
"mock >= 4.0.3", "mock >= 4.0.3",
"model_mommy >= 2.0", "model_mommy >= 2.0",
"anything ==0.2.1", "anything ==0.2.1",
"black >= 22.6", "black >= 22.6",
"isort >= 5.10", "isort >= 5.10",
"parameterized ==0.8.1", "parameterized ==0.8.1",
] ]
[project.optional-dependencies] [project.optional-dependencies]
extra = [ extra = [
# contrib optional dependencies # contrib optional dependencies
# install with 'pip install evennia[extra]` # install with 'pip install evennia[extra]`
# crypto libraries for ssh support # crypto libraries for ssh support
"cryptography >= 2.8", "cryptography >= 2.8",
"pyasn1 >= 0.4.8", "pyasn1 >= 0.4.8",
"bcrypt >= 3.1.7", "bcrypt >= 3.1.7",
# Telnet-SSL support # Telnet-SSL support
"pyopenssl >= 19.1", "pyopenssl >= 19.1",
"service_identity >= 18.1.0", "service_identity >= 18.1.0",
# AWS storage contrib # AWS storage contrib
"boto3 >= 1.4.4", "boto3 >= 1.4.4",
"botocore >= 1.15", "botocore >= 1.15",
# Jupyter Notebook support # Jupyter Notebook support
"jupyter >= 1.0.0", "jupyter >= 1.0.0",
"ipython >= 7.19.0", "ipython >= 7.19.0",
"django-extensions >= 3.1.0", "django-extensions >= 3.1.0",
# xyzroom contrib # xyzroom contrib
"scipy == 1.9.3", "scipy == 1.9.3",
# Git contrib # Git contrib
"gitpython >= 3.1.27" "gitpython >= 3.1.27",
] ]
[project.urls] [project.urls]
@ -133,3 +152,20 @@ exclude = '''
) )
''' '''
[tool.coverage]
[tool.coverage.run]
concurrency = ["multiprocessing"]
parallel = true
source = ["evennia"]
omit = [
"*/migrations/*",
"*/urls.py",
"*/test*.py",
"*.sh",
"*.txt",
"*.md",
"*.pyc",
"*.service",
]