Merge branch 'main' into evadventure_work
This commit is contained in:
commit
1f3d4ed840
23 changed files with 112 additions and 93 deletions
11
CHANGELOG.md
11
CHANGELOG.md
|
|
@ -1,13 +1,18 @@
|
||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
## Main branch
|
## Evennia 1.3.0
|
||||||
|
|
||||||
- Feature: Better ANSI color fallbacks (InspectorCaracal)
|
Apr 29, 2023
|
||||||
|
|
||||||
|
- Feature: Better ANSI color fallbacks (InspectorCaracal).
|
||||||
- Feature: Add support for saving `deque` with `maxlen` to Attributes (before
|
- Feature: Add support for saving `deque` with `maxlen` to Attributes (before
|
||||||
`maxlen` was ignored).
|
`maxlen` was ignored).
|
||||||
- Tools: More unit tests for scripts (Storsorken)
|
- Fix: The username validator did not display errors correctly in web
|
||||||
|
registration form.
|
||||||
- Fix: Components contrib had issues with inherited typeclasses (ChrisLR)
|
- Fix: Components contrib had issues with inherited typeclasses (ChrisLR)
|
||||||
- Fix: f-string fix in clothing contrib (aMiss-aWry)
|
- Fix: f-string fix in clothing contrib (aMiss-aWry)
|
||||||
|
- Fix: Have `EvenniaTestCase` properly flush idmapper cache (bradleymarques)
|
||||||
|
- Tools: More unit tests for scripts (Storsorken)
|
||||||
- Docs: Made separate doc pages for Exits, Characters and Rooms. Expanded on how
|
- Docs: Made separate doc pages for Exits, Characters and Rooms. Expanded on how
|
||||||
to change the description of an in-game object with templating.
|
to change the description of an in-game object with templating.
|
||||||
- Docs: A multitude of doc issues and typos fixed.
|
- Docs: A multitude of doc issues and typos fixed.
|
||||||
|
|
|
||||||
|
|
@ -58,10 +58,10 @@ _multiversion-check-env:
|
||||||
@EVDIR=$(EVDIR) EVGAMEDIR=$(EVGAMEDIR) bash -e checkenv.sh multiversion
|
@EVDIR=$(EVDIR) EVGAMEDIR=$(EVGAMEDIR) bash -e checkenv.sh multiversion
|
||||||
|
|
||||||
_clean_api_index:
|
_clean_api_index:
|
||||||
rm source/api/*
|
rm -f source/api/*
|
||||||
|
|
||||||
_clean_api_rsts:
|
_clean_api_rsts:
|
||||||
rm source/api/*.rst
|
rm -f source/api/*.rst
|
||||||
|
|
||||||
# remove superfluos 'module' and 'package' text from api headers
|
# remove superfluos 'module' and 'package' text from api headers
|
||||||
_reformat_apidoc_headers:
|
_reformat_apidoc_headers:
|
||||||
|
|
|
||||||
|
|
@ -1,13 +1,21 @@
|
||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
## Main branch
|
## Evennia 1.3.0
|
||||||
|
|
||||||
|
Apr 29, 2023
|
||||||
|
|
||||||
|
- Feature: Better ANSI color fallbacks (InspectorCaracal).
|
||||||
- Feature: Add support for saving `deque` with `maxlen` to Attributes (before
|
- Feature: Add support for saving `deque` with `maxlen` to Attributes (before
|
||||||
`maxlen` was ignored).
|
`maxlen` was ignored).
|
||||||
- Fix: More unit tests for scripts (Storsorken)
|
- Fix: The username validator did not display errors correctly in web
|
||||||
|
registration form.
|
||||||
|
- Fix: Components contrib had issues with inherited typeclasses (ChrisLR)
|
||||||
|
- Fix: f-string fix in clothing contrib (aMiss-aWry)
|
||||||
|
- Fix: Have `EvenniaTestCase` properly flush idmapper cache (bradleymarques)
|
||||||
|
- Tools: More unit tests for scripts (Storsorken)
|
||||||
- Docs: Made separate doc pages for Exits, Characters and Rooms. Expanded on how
|
- Docs: Made separate doc pages for Exits, Characters and Rooms. Expanded on how
|
||||||
to change the description of an in-game object with templating.
|
to change the description of an in-game object with templating.
|
||||||
- Docs: Fixed a multitude of doc issues.
|
- Docs: A multitude of doc issues and typos fixed.
|
||||||
|
|
||||||
## Evennia 1.2.1
|
## Evennia 1.2.1
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -106,7 +106,7 @@ 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 settings.py world.tests
|
||||||
|
|
||||||
or a specific class:
|
or a specific class:
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -17,8 +17,8 @@ Channels can be used both for chats between [Accounts](./Accounts.md) and betwee
|
||||||
- Private guild channels for planning and organization (IC/OOC depending on game)
|
- Private guild channels for planning and organization (IC/OOC depending on game)
|
||||||
- Cyberpunk-style retro chat rooms (IC)
|
- Cyberpunk-style retro chat rooms (IC)
|
||||||
- In-game radio channels (IC)
|
- In-game radio channels (IC)
|
||||||
- Group telephathy (IC)
|
- Group telepathy (IC)
|
||||||
- Walkie talkies (IC)
|
- Walkie-talkies (IC)
|
||||||
|
|
||||||
```{versionchanged} 1.0
|
```{versionchanged} 1.0
|
||||||
|
|
||||||
|
|
@ -150,7 +150,7 @@ To create/destroy a new channel on the fly you can do
|
||||||
Aliases are optional but can be good for obvious shortcuts everyone may want to
|
Aliases are optional but can be good for obvious shortcuts everyone may want to
|
||||||
use. The description is used in channel-listings. You will automatically join a
|
use. The description is used in channel-listings. You will automatically join a
|
||||||
channel you created and will be controlling it. You can also use `channel/desc` to
|
channel you created and will be controlling it. You can also use `channel/desc` to
|
||||||
change the description on a channel you wnn later.
|
change the description on a channel you own later.
|
||||||
|
|
||||||
If you control a channel you can also kick people off it:
|
If you control a channel you can also kick people off it:
|
||||||
|
|
||||||
|
|
@ -223,7 +223,7 @@ channels you could override the `help` command and change the lockstring to:
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
Add this custom command to your default cmdset and regular users wil now get an
|
Add this custom command to your default cmdset and regular users will now get an
|
||||||
access-denied error when trying to use use these switches.
|
access-denied error when trying to use use these switches.
|
||||||
|
|
||||||
## Using channels in code
|
## Using channels in code
|
||||||
|
|
@ -263,7 +263,7 @@ below:
|
||||||
3. `channel.at_post_channel_msg(message, **kwargs)`
|
3. `channel.at_post_channel_msg(message, **kwargs)`
|
||||||
|
|
||||||
Note that `Accounts` and `Objects` both have their have separate sets of hooks.
|
Note that `Accounts` and `Objects` both have their have separate sets of hooks.
|
||||||
So make sure you modify the set actually used by your subcribers (or both).
|
So make sure you modify the set actually used by your subscribers (or both).
|
||||||
Default channels all use `Account` subscribers.
|
Default channels all use `Account` subscribers.
|
||||||
|
|
||||||
### Channel class
|
### Channel class
|
||||||
|
|
@ -379,7 +379,7 @@ Notable `Channel` hooks:
|
||||||
a class-method that will happily remove found channel-aliases from the user linked to _any_
|
a class-method that will happily remove found channel-aliases from the user linked to _any_
|
||||||
channel, not only from the channel the method is called on.
|
channel, not only from the channel the method is called on.
|
||||||
- `pre_join_channel(subscriber)` - if this returns `False`, connection will be refused.
|
- `pre_join_channel(subscriber)` - if this returns `False`, connection will be refused.
|
||||||
- `post_join_channel(subscriber)` - by default this sets up a users's channel-nicks/aliases.
|
- `post_join_channel(subscriber)` - by default this sets up a users' channel-nicks/aliases.
|
||||||
- `pre_leave_channel(subscriber)` - if this returns `False`, the user is not allowed to leave.
|
- `pre_leave_channel(subscriber)` - if this returns `False`, the user is not allowed to leave.
|
||||||
- `post_leave_channel(subscriber)` - this will clean up any channel aliases/nicks of the user.
|
- `post_leave_channel(subscriber)` - this will clean up any channel aliases/nicks of the user.
|
||||||
- `delete` the standard typeclass-delete mechanism will also automatically un-subscribe all
|
- `delete` the standard typeclass-delete mechanism will also automatically un-subscribe all
|
||||||
|
|
|
||||||
|
|
@ -19,6 +19,8 @@
|
||||||
|
|
||||||
*Exits* are in-game [Objects](./Objects.md) connecting other objects (usually [Rooms](./Rooms.md)) together.
|
*Exits* are in-game [Objects](./Objects.md) connecting other objects (usually [Rooms](./Rooms.md)) together.
|
||||||
|
|
||||||
|
> Note that Exits are one-way objects, so in order for two Rooms to be linked bi-directionally, there will need to be two exits.
|
||||||
|
|
||||||
An object named `north` or `in` might be exits, as well as `door`, `portal` or `jump out the window`.
|
An object named `north` or `in` might be exits, as well as `door`, `portal` or `jump out the window`.
|
||||||
|
|
||||||
An exit has two things that separate them from other objects.
|
An exit has two things that separate them from other objects.
|
||||||
|
|
@ -29,7 +31,7 @@ The default exit functionality is all defined on the [DefaultExit](DefaultExit)
|
||||||
|
|
||||||
Exits are [locked](./Locks.md) using an `access_type` called *traverse* and also make use of a few hook methods for giving feedback if the traversal fails. See `evennia.DefaultExit` for more info.
|
Exits are [locked](./Locks.md) using an `access_type` called *traverse* and also make use of a few hook methods for giving feedback if the traversal fails. See `evennia.DefaultExit` for more info.
|
||||||
|
|
||||||
Exits are normally overridden on a case-by-case basis, but if you want to change the default exit createad by rooms like `dig` , `tunnel` or `open` you can change it in settings:
|
Exits are normally overridden on a case-by-case basis, but if you want to change the default exit created by rooms like `dig`, `tunnel` or `open` you can change it in settings:
|
||||||
|
|
||||||
BASE_EXIT_TYPECLASS = "typeclasses.exits.Exit"
|
BASE_EXIT_TYPECLASS = "typeclasses.exits.Exit"
|
||||||
|
|
||||||
|
|
@ -53,3 +55,7 @@ The process of traversing an exit is as follows:
|
||||||
1. On the Exit object, `at_post_traverse(obj, source)` is triggered.
|
1. On the Exit object, `at_post_traverse(obj, source)` is triggered.
|
||||||
|
|
||||||
If the move fails for whatever reason, the Exit will look for an Attribute `err_traverse` on itself and display this as an error message. If this is not found, the Exit will instead call `at_failed_traverse(obj)` on itself.
|
If the move fails for whatever reason, the Exit will look for an Attribute `err_traverse` on itself and display this as an error message. If this is not found, the Exit will instead call `at_failed_traverse(obj)` on itself.
|
||||||
|
|
||||||
|
### Creating Exits in code
|
||||||
|
|
||||||
|
For an example of how to create Exits programatically please see [this guide](../Howtos/Beginner-Tutorial/Part1/Beginner-Tutorial-Creating-Things.md#linking-exits-and-rooms-in-code).
|
||||||
|
|
|
||||||
|
|
@ -104,7 +104,7 @@ Below are the access_types checked by the default commandset.
|
||||||
- `search` - this controls if the object can be found with the
|
- `search` - this controls if the object can be found with the
|
||||||
`DefaultObject.search` method (usually referred to with `caller.search`
|
`DefaultObject.search` method (usually referred to with `caller.search`
|
||||||
in Commands). This is how to create entirely 'undetectable' in-game objects.
|
in Commands). This is how to create entirely 'undetectable' in-game objects.
|
||||||
If not setting this lock excplicitly, all objects are assumed searchable.
|
If not setting this lock explicitly, all objects are assumed searchable.
|
||||||
Note that if you are aiming to make some _permanently invisible game system,
|
Note that if you are aiming to make some _permanently invisible game system,
|
||||||
using a [Script](./Scripts.md) is a better bet.
|
using a [Script](./Scripts.md) is a better bet.
|
||||||
- `get`- who may pick up the object and carry it around.
|
- `get`- who may pick up the object and carry it around.
|
||||||
|
|
@ -330,7 +330,7 @@ error message. Sounds good! Let's start by setting that on the box:
|
||||||
Next we need to craft a Lock of type *get* on our box. We want it to only be passed if the accessing
|
Next we need to craft a Lock of type *get* on our box. We want it to only be passed if the accessing
|
||||||
object has the attribute *strength* of the right value. For this we would need to create a lock
|
object has the attribute *strength* of the right value. For this we would need to create a lock
|
||||||
function that checks if attributes have a value greater than a given value. Luckily there is already
|
function that checks if attributes have a value greater than a given value. Luckily there is already
|
||||||
such a one included in evennia (see `evennia/locks/lockfuncs.py`), called `attr_gt`.
|
such a one included in Evennia (see `evennia/locks/lockfuncs.py`), called `attr_gt`.
|
||||||
|
|
||||||
So the lock string will look like this: `get:attr_gt(strength, 50)`. We put this on the box now:
|
So the lock string will look like this: `get:attr_gt(strength, 50)`. We put this on the box now:
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -88,7 +88,7 @@ If you cannot find your language in `evennia/evennia/locale/` it's because noone
|
||||||
has translated it yet. Alternatively you might have the language but find the
|
has translated it yet. Alternatively you might have the language but find the
|
||||||
translation bad ... You are welcome to help improve the situation!
|
translation bad ... You are welcome to help improve the situation!
|
||||||
|
|
||||||
To start a new translation you need to first have cloned the Evennia repositry
|
To start a new translation you need to first have cloned the Evennia repository
|
||||||
with GIT and activated a python virtualenv as described on the
|
with GIT and activated a python virtualenv as described on the
|
||||||
[Setup Quickstart](../Setup/Installation.md) page.
|
[Setup Quickstart](../Setup/Installation.md) page.
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,24 +1,26 @@
|
||||||
# Components
|
# Components
|
||||||
|
|
||||||
_Contrib by ChrisLR 2021_
|
Contrib by ChrisLR, 2021
|
||||||
|
|
||||||
# The Components Contrib
|
Expand typeclasses using a components/composition approach.
|
||||||
|
|
||||||
|
## The Components Contrib
|
||||||
|
|
||||||
This contrib introduces Components and Composition to Evennia.
|
This contrib introduces Components and Composition to Evennia.
|
||||||
Each 'Component' class represents a feature that will be 'enabled' on a typeclass instance.
|
Each 'Component' class represents a feature that will be 'enabled' on a typeclass instance.
|
||||||
You can register these components on an entire typeclass or a single object at runtime.
|
You can register these components on an entire typeclass or a single object at runtime.
|
||||||
It supports both persisted attributes and in-memory attributes by using Evennia's AttributeHandler.
|
It supports both persisted attributes and in-memory attributes by using Evennia's AttributeHandler.
|
||||||
|
|
||||||
# Pros
|
## Pros
|
||||||
- You can reuse a feature across multiple typeclasses without inheritance
|
- You can reuse a feature across multiple typeclasses without inheritance
|
||||||
- You can cleanly organize each feature into a self-contained class.
|
- You can cleanly organize each feature into a self-contained class.
|
||||||
- You can check if your object supports a feature without checking its instance.
|
- You can check if your object supports a feature without checking its instance.
|
||||||
|
|
||||||
# Cons
|
## Cons
|
||||||
- It introduces additional complexity.
|
- It introduces additional complexity.
|
||||||
- A host typeclass instance is required.
|
- A host typeclass instance is required.
|
||||||
|
|
||||||
# How to install
|
## How to install
|
||||||
|
|
||||||
To enable component support for a typeclass,
|
To enable component support for a typeclass,
|
||||||
import and inherit the ComponentHolderMixin, similar to this
|
import and inherit the ComponentHolderMixin, similar to this
|
||||||
|
|
@ -126,7 +128,7 @@ from typeclasses.components import health
|
||||||
```
|
```
|
||||||
Both of the above examples will work.
|
Both of the above examples will work.
|
||||||
|
|
||||||
# Full Example
|
## Full Example
|
||||||
```python
|
```python
|
||||||
from evennia.contrib.base_systems import components
|
from evennia.contrib.base_systems import components
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -111,9 +111,9 @@ Additional color markup styles for Evennia (extending or replacing the default
|
||||||
|
|
||||||
### `components`
|
### `components`
|
||||||
|
|
||||||
__Contrib by ChrisLR 2021__
|
_Contrib by ChrisLR, 2021_
|
||||||
|
|
||||||
# The Components Contrib
|
Expand typeclasses using a components/composition approach.
|
||||||
|
|
||||||
[Read the documentation](./Contrib-Components.md) - [Browse the Code](evennia.contrib.base_systems.components)
|
[Read the documentation](./Contrib-Components.md) - [Browse the Code](evennia.contrib.base_systems.components)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -153,7 +153,7 @@ Let's make us one of _those_!
|
||||||
|
|
||||||
create/drop button:tutorials.red_button.RedButton
|
create/drop button:tutorials.red_button.RedButton
|
||||||
|
|
||||||
The same way we did with the Script Earler, we specify a "Python-path" to the Python code we want Evennia to use for creating the object. There you go - one red button.
|
The same way we did with the Script earlier, we specify a "Python-path" to the Python code we want Evennia to use for creating the object. There you go - one red button.
|
||||||
|
|
||||||
The RedButton is an example object intended to show off a few of Evennia's features. You will find that the [Typeclass](../../../Components/Typeclasses.md) and [Commands](../../../Components/Commands.md) controlling it are inside [evennia/contrib/tutorials/red_button](../../../api/evennia.contrib.tutorials.red_button.md)
|
The RedButton is an example object intended to show off a few of Evennia's features. You will find that the [Typeclass](../../../Components/Typeclasses.md) and [Commands](../../../Components/Commands.md) controlling it are inside [evennia/contrib/tutorials/red_button](../../../api/evennia.contrib.tutorials.red_button.md)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -320,7 +320,7 @@ class EvAdventureRollEngine:
|
||||||
defender_defense = getattr(defender, defense_type.value, 1) + 10
|
defender_defense = getattr(defender, defense_type.value, 1) + 10
|
||||||
result, quality = self.saving_throw(attacker, bonus_type=attack_type,
|
result, quality = self.saving_throw(attacker, bonus_type=attack_type,
|
||||||
target=defender_defense,
|
target=defender_defense,
|
||||||
advantage=advantave, disadvantage=disadvantage)
|
advantage=advantage, disadvantage=disadvantage)
|
||||||
|
|
||||||
return result, quality
|
return result, quality
|
||||||
```
|
```
|
||||||
|
|
@ -584,8 +584,8 @@ class TestEvAdventureRuleEngine(BaseEvenniaTest):
|
||||||
@patch("evadventure.rules.randint")
|
@patch("evadventure.rules.randint")
|
||||||
def test_roll(self, mock_randint):
|
def test_roll(self, mock_randint):
|
||||||
mock_randint.return_value = 4
|
mock_randint.return_value = 4
|
||||||
self.assertEqual(self.roll_engine.roll("1d6", 4)
|
self.assertEqual(self.roll_engine.roll("1d6", 4))
|
||||||
self.assertEqual(self.roll_engine.roll("2d6", 2 * 4)
|
self.assertEqual(self.roll_engine.roll("2d6"), 2 * 4)
|
||||||
|
|
||||||
# test of the other rule methods below ...
|
# test of the other rule methods below ...
|
||||||
```
|
```
|
||||||
|
|
|
||||||
|
|
@ -54,7 +54,7 @@ class NPCMerchant(Object):
|
||||||
def open_shop(self, shopper):
|
def open_shop(self, shopper):
|
||||||
menunodes = {} # TODO!
|
menunodes = {} # TODO!
|
||||||
shopname = self.db.shopname or "The shop"
|
shopname = self.db.shopname or "The shop"
|
||||||
EvMenu(shopper, menunodes, startnode="shop_start",
|
EvMenu(shopper, menunodes, startnode="shopfront",
|
||||||
shopname=shopname, shopkeeper=self, wares=self.contents)
|
shopname=shopname, shopkeeper=self, wares=self.contents)
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
@ -215,7 +215,7 @@ class NPCMerchant(Object):
|
||||||
"inspect_and_buy": node_inspect_and_buy
|
"inspect_and_buy": node_inspect_and_buy
|
||||||
}
|
}
|
||||||
shopname = self.db.shopname or "The shop"
|
shopname = self.db.shopname or "The shop"
|
||||||
EvMenu(shopper, menunodes, startnode="shop_start",
|
EvMenu(shopper, menunodes, startnode="shopfront",
|
||||||
shopname=shopname, shopkeeper=self, wares=self.contents)
|
shopname=shopname, shopkeeper=self, wares=self.contents)
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
|
||||||
|
|
@ -78,6 +78,7 @@ If `localhost` doesn't work when trying to connect to your local game, try `127.
|
||||||
## Mac Troubleshooting
|
## Mac Troubleshooting
|
||||||
|
|
||||||
- Some Mac users have reported not being able to connect to `localhost` (i.e. your own computer). If so, try to connect to `127.0.0.1` instead, which is the same thing. Use port 4000 from mud clients and port 4001 from the web browser as usual.
|
- Some Mac users have reported not being able to connect to `localhost` (i.e. your own computer). If so, try to connect to `127.0.0.1` instead, which is the same thing. Use port 4000 from mud clients and port 4001 from the web browser as usual.
|
||||||
|
- If you get a `MemoryError` when starting Evennia, or when looking at the log, this may be due to an sqlite versioning issue. [A user in our forums](https://github.com/evennia/evennia/discussions/2638#discussioncomment-3630761) found a working solution for this. [Here](https://github.com/evennia/evennia/issues/3120#issuecomment-1442540538) is another variation to solve it.
|
||||||
|
|
||||||
## Windows Troubleshooting
|
## Windows Troubleshooting
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1 +1 @@
|
||||||
1.2.1
|
1.3.0
|
||||||
|
|
|
||||||
|
|
@ -20,7 +20,6 @@ from django.core.exceptions import ImproperlyConfigured, ValidationError
|
||||||
from django.utils import timezone
|
from django.utils import timezone
|
||||||
from django.utils.module_loading import import_string
|
from django.utils.module_loading import import_string
|
||||||
from django.utils.translation import gettext as _
|
from django.utils.translation import gettext as _
|
||||||
|
|
||||||
from evennia.accounts.manager import AccountManager
|
from evennia.accounts.manager import AccountManager
|
||||||
from evennia.accounts.models import AccountDB
|
from evennia.accounts.models import AccountDB
|
||||||
from evennia.commands.cmdsethandler import CmdSetHandler
|
from evennia.commands.cmdsethandler import CmdSetHandler
|
||||||
|
|
@ -38,13 +37,7 @@ from evennia.typeclasses.attributes import ModelAttributeBackend, NickHandler
|
||||||
from evennia.typeclasses.models import TypeclassBase
|
from evennia.typeclasses.models import TypeclassBase
|
||||||
from evennia.utils import class_from_module, create, logger
|
from evennia.utils import class_from_module, create, logger
|
||||||
from evennia.utils.optionhandler import OptionHandler
|
from evennia.utils.optionhandler import OptionHandler
|
||||||
from evennia.utils.utils import (
|
from evennia.utils.utils import is_iter, lazy_property, make_iter, to_str, variable_from_module
|
||||||
is_iter,
|
|
||||||
lazy_property,
|
|
||||||
make_iter,
|
|
||||||
to_str,
|
|
||||||
variable_from_module,
|
|
||||||
)
|
|
||||||
|
|
||||||
__all__ = ("DefaultAccount", "DefaultGuest")
|
__all__ = ("DefaultAccount", "DefaultGuest")
|
||||||
|
|
||||||
|
|
@ -509,7 +502,6 @@ class DefaultAccount(AccountDB, metaclass=TypeclassBase):
|
||||||
Returns:
|
Returns:
|
||||||
validators (list): List of instantiated Validator objects.
|
validators (list): List of instantiated Validator objects.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
objs = []
|
objs = []
|
||||||
for validator in validator_config:
|
for validator in validator_config:
|
||||||
try:
|
try:
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,6 @@ Communication commands:
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
|
|
||||||
from evennia.accounts import bots
|
from evennia.accounts import bots
|
||||||
from evennia.accounts.models import AccountDB
|
from evennia.accounts.models import AccountDB
|
||||||
from evennia.comms.comms import DefaultChannel
|
from evennia.comms.comms import DefaultChannel
|
||||||
|
|
@ -777,7 +776,6 @@ class CmdChannel(COMMAND_DEFAULT_CLASS):
|
||||||
maxwidth=_DEFAULT_WIDTH,
|
maxwidth=_DEFAULT_WIDTH,
|
||||||
)
|
)
|
||||||
for chan in subscribed:
|
for chan in subscribed:
|
||||||
|
|
||||||
locks = "-"
|
locks = "-"
|
||||||
chanid = "-"
|
chanid = "-"
|
||||||
if chan.access(self.caller, "control"):
|
if chan.access(self.caller, "control"):
|
||||||
|
|
@ -1158,7 +1156,6 @@ class CmdChannel(COMMAND_DEFAULT_CLASS):
|
||||||
reason = reason[0].strip() if reason else ""
|
reason = reason[0].strip() if reason else ""
|
||||||
|
|
||||||
for chan in channels:
|
for chan in channels:
|
||||||
|
|
||||||
if not chan.access(caller, "control"):
|
if not chan.access(caller, "control"):
|
||||||
self.msg(f"You need 'control'-access to boot a user from {chan.key}.")
|
self.msg(f"You need 'control'-access to boot a user from {chan.key}.")
|
||||||
return
|
return
|
||||||
|
|
@ -1245,9 +1242,11 @@ class CmdChannel(COMMAND_DEFAULT_CLASS):
|
||||||
)
|
)
|
||||||
ask_yes_no(
|
ask_yes_no(
|
||||||
caller,
|
caller,
|
||||||
|
(
|
||||||
f"Are you sure you want to ban user {target.key} from "
|
f"Are you sure you want to ban user {target.key} from "
|
||||||
f"channel(s) {channames} (make sure name/channels are correct{reasonwarn}) "
|
f"channel(s) {channames} (make sure name/channels are correct{reasonwarn}) "
|
||||||
"{options}?",
|
"{options}?"
|
||||||
|
),
|
||||||
_ban_user,
|
_ban_user,
|
||||||
"Aborted.",
|
"Aborted.",
|
||||||
)
|
)
|
||||||
|
|
@ -1360,7 +1359,7 @@ class CmdPage(COMMAND_DEFAULT_CLASS):
|
||||||
targets.append(target_obj)
|
targets.append(target_obj)
|
||||||
message = self.rhs.strip()
|
message = self.rhs.strip()
|
||||||
else:
|
else:
|
||||||
target, *message = self.args.split(" ", 4)
|
target, *message = self.args.split(" ", 1)
|
||||||
if target and target.isnumeric():
|
if target and target.isnumeric():
|
||||||
# a number to specify a historic page
|
# a number to specify a historic page
|
||||||
number = int(target)
|
number = int(target)
|
||||||
|
|
@ -1970,7 +1969,8 @@ class CmdDiscord2Chan(COMMAND_DEFAULT_CLASS):
|
||||||
|
|
||||||
if not discord_bot.is_typeclass(settings.DISCORD_BOT_CLASS, exact=True):
|
if not discord_bot.is_typeclass(settings.DISCORD_BOT_CLASS, exact=True):
|
||||||
self.msg(
|
self.msg(
|
||||||
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"
|
||||||
|
f" not match {settings.DISCORD_BOT_CLASS} in settings!"
|
||||||
)
|
)
|
||||||
|
|
||||||
if "start" in self.switches:
|
if "start" in self.switches:
|
||||||
|
|
@ -1984,13 +1984,15 @@ class CmdDiscord2Chan(COMMAND_DEFAULT_CLASS):
|
||||||
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(
|
||||||
f"Messages to Evennia |wwill {'' if discord_bot.db.tag_guild else 'not '}|ninclude the Discord server."
|
f"Messages to Evennia |wwill {'' if discord_bot.db.tag_guild else 'not '}|ninclude"
|
||||||
|
" the Discord server."
|
||||||
)
|
)
|
||||||
return
|
return
|
||||||
if "channel" in self.switches:
|
if "channel" in self.switches:
|
||||||
discord_bot.db.tag_channel = not discord_bot.db.tag_channel
|
discord_bot.db.tag_channel = not discord_bot.db.tag_channel
|
||||||
self.msg(
|
self.msg(
|
||||||
f"Relayed messages |wwill {'' if discord_bot.db.tag_channel else 'not '}|ninclude the originating channel."
|
f"Relayed messages |wwill {'' if discord_bot.db.tag_channel else 'not '}|ninclude"
|
||||||
|
" the originating channel."
|
||||||
)
|
)
|
||||||
return
|
return
|
||||||
|
|
||||||
|
|
@ -2029,7 +2031,8 @@ class CmdDiscord2Chan(COMMAND_DEFAULT_CLASS):
|
||||||
dc_chan_names = discord_bot.attributes.get("discord_channels", {})
|
dc_chan_names = discord_bot.attributes.get("discord_channels", {})
|
||||||
dc_info = dc_chan_names.get(dc_chan, {"name": "unknown", "guild": "unknown"})
|
dc_info = dc_chan_names.get(dc_chan, {"name": "unknown", "guild": "unknown"})
|
||||||
self.msg(
|
self.msg(
|
||||||
f"Removed link between {ev_chan} and #{dc_info.get('name','?')}@{dc_info.get('guild','?')}"
|
f"Removed link between {ev_chan} and"
|
||||||
|
f" #{dc_info.get('name','?')}@{dc_info.get('guild','?')}"
|
||||||
)
|
)
|
||||||
return
|
return
|
||||||
else:
|
else:
|
||||||
|
|
|
||||||
|
|
@ -1,24 +1,26 @@
|
||||||
# Components
|
# Components
|
||||||
|
|
||||||
_Contrib by ChrisLR 2021_
|
Contrib by ChrisLR, 2021
|
||||||
|
|
||||||
# The Components Contrib
|
Expand typeclasses using a components/composition approach.
|
||||||
|
|
||||||
|
## The Components Contrib
|
||||||
|
|
||||||
This contrib introduces Components and Composition to Evennia.
|
This contrib introduces Components and Composition to Evennia.
|
||||||
Each 'Component' class represents a feature that will be 'enabled' on a typeclass instance.
|
Each 'Component' class represents a feature that will be 'enabled' on a typeclass instance.
|
||||||
You can register these components on an entire typeclass or a single object at runtime.
|
You can register these components on an entire typeclass or a single object at runtime.
|
||||||
It supports both persisted attributes and in-memory attributes by using Evennia's AttributeHandler.
|
It supports both persisted attributes and in-memory attributes by using Evennia's AttributeHandler.
|
||||||
|
|
||||||
# Pros
|
## Pros
|
||||||
- You can reuse a feature across multiple typeclasses without inheritance
|
- You can reuse a feature across multiple typeclasses without inheritance
|
||||||
- You can cleanly organize each feature into a self-contained class.
|
- You can cleanly organize each feature into a self-contained class.
|
||||||
- You can check if your object supports a feature without checking its instance.
|
- You can check if your object supports a feature without checking its instance.
|
||||||
|
|
||||||
# Cons
|
## Cons
|
||||||
- It introduces additional complexity.
|
- It introduces additional complexity.
|
||||||
- A host typeclass instance is required.
|
- A host typeclass instance is required.
|
||||||
|
|
||||||
# How to install
|
## How to install
|
||||||
|
|
||||||
To enable component support for a typeclass,
|
To enable component support for a typeclass,
|
||||||
import and inherit the ComponentHolderMixin, similar to this
|
import and inherit the ComponentHolderMixin, similar to this
|
||||||
|
|
@ -126,7 +128,7 @@ from typeclasses.components import health
|
||||||
```
|
```
|
||||||
Both of the above examples will work.
|
Both of the above examples will work.
|
||||||
|
|
||||||
# Full Example
|
## Full Example
|
||||||
```python
|
```python
|
||||||
from evennia.contrib.base_systems import components
|
from evennia.contrib.base_systems import components
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,6 @@ import re
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.core.exceptions import ValidationError
|
from django.core.exceptions import ValidationError
|
||||||
from django.utils.translation import gettext as _
|
from django.utils.translation import gettext as _
|
||||||
|
|
||||||
from evennia.accounts.models import AccountDB
|
from evennia.accounts.models import AccountDB
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -24,7 +23,6 @@ class EvenniaUsernameAvailabilityValidator:
|
||||||
raises ValidationError otherwise.
|
raises ValidationError otherwise.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# Check guest list
|
# Check guest list
|
||||||
if settings.GUEST_LIST and username.lower() in (
|
if settings.GUEST_LIST and username.lower() in (
|
||||||
guest.lower() for guest in settings.GUEST_LIST
|
guest.lower() for guest in settings.GUEST_LIST
|
||||||
|
|
@ -45,8 +43,7 @@ class EvenniaPasswordValidator:
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
regex=r"^[\w. @+\-',]+$",
|
regex=r"^[\w. @+\-',]+$",
|
||||||
policy="Password should contain a mix of letters, "
|
policy="Password should contain a mix of letters, spaces, digits and @/./+/-/_/'/, only.",
|
||||||
"spaces, digits and @/./+/-/_/'/, only.",
|
|
||||||
):
|
):
|
||||||
"""
|
"""
|
||||||
Constructs a standard Django password validator.
|
Constructs a standard Django password validator.
|
||||||
|
|
|
||||||
|
|
@ -558,9 +558,19 @@ class EvenniaTestCase(TestCase):
|
||||||
"""
|
"""
|
||||||
For use with gamedir settings; Just like the normal test case, only for naming consistency.
|
For use with gamedir settings; Just like the normal test case, only for naming consistency.
|
||||||
|
|
||||||
|
Notes:
|
||||||
|
|
||||||
|
- Inheriting from this class will bypass EvenniaTestMixin, and therefore
|
||||||
|
not setup some default objects. This can result in faster tests.
|
||||||
|
|
||||||
|
- If you do inherit from this class for your unit tests, and have
|
||||||
|
overridden the tearDown() method, please also call flush_cache(). Not
|
||||||
|
doing so will result in flakey and order-dependent tests due to the
|
||||||
|
Django ID cache not being flushed.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
pass
|
def tearDown(self) -> None:
|
||||||
|
flush_cache()
|
||||||
|
|
||||||
|
|
||||||
@override_settings(**DEFAULT_SETTINGS)
|
@override_settings(**DEFAULT_SETTINGS)
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,6 @@ from django.conf import settings
|
||||||
from django.contrib import messages
|
from django.contrib import messages
|
||||||
from django.http import HttpResponseRedirect
|
from django.http import HttpResponseRedirect
|
||||||
from django.urls import reverse_lazy
|
from django.urls import reverse_lazy
|
||||||
|
|
||||||
from evennia.utils import class_from_module
|
from evennia.utils import class_from_module
|
||||||
from evennia.web.website import forms
|
from evennia.web.website import forms
|
||||||
|
|
||||||
|
|
@ -56,22 +55,16 @@ class AccountCreateView(AccountMixin, EvenniaCreateView):
|
||||||
password = form.cleaned_data["password1"]
|
password = form.cleaned_data["password1"]
|
||||||
email = form.cleaned_data.get("email", "")
|
email = form.cleaned_data.get("email", "")
|
||||||
|
|
||||||
# Create account
|
# Create account. This also runs all validations on the username/password.
|
||||||
account, errs = self.typeclass.create(username=username, password=password, email=email)
|
account, errs = self.typeclass.create(username=username, password=password, email=email)
|
||||||
|
|
||||||
# If unsuccessful, display error messages to user
|
|
||||||
if not account:
|
if not account:
|
||||||
[messages.error(self.request, err) for err in errs]
|
# password validation happens earlier, only username checks appear here.
|
||||||
|
form.add_error("username", ", ".join(errs))
|
||||||
# Call the Django "form failure" hook
|
|
||||||
return self.form_invalid(form)
|
return self.form_invalid(form)
|
||||||
|
else:
|
||||||
# Inform user of success
|
# Inform user of success
|
||||||
messages.success(
|
messages.success(
|
||||||
self.request,
|
self.request, f"Your account '{account.name}' was successfully created!"
|
||||||
"Your account '%s' was successfully created! "
|
|
||||||
"You may log in using it now." % account.name,
|
|
||||||
)
|
)
|
||||||
|
|
||||||
# Redirect the user to the login page
|
|
||||||
return HttpResponseRedirect(self.success_url)
|
return HttpResponseRedirect(self.success_url)
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
||||||
|
|
||||||
[project]
|
[project]
|
||||||
name = "evennia"
|
name = "evennia"
|
||||||
version = "1.2.1"
|
version = "1.3.0"
|
||||||
maintainers = [{ name = "Griatch", email = "griatch@gmail.com" }]
|
maintainers = [{ 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"
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue