This tutorial assumes you have a basic understanding of the web folder structure. If you don’t, please read the page on the website and make sure you understand it before you come back!
+
+
For this guide, we’ll be adding an inventory action to the characters endpoint, showing all objects being worn and carried by a character.
+
+
“worn versus carried” isn’t built into core Evennia, but it’s a common thing to add. This guide uses a .db.worn attribute to identify gear, but will explain how to reference your own mechanic too.
+
+
The first thing you should do is review the REST API documentation page. It’s not very long, but it covers how to turn the API on and which parts of django you should be familiar with.
+
Once you’ve read that and successfully visited /api/characters/, it’s time to get started!
The first thing you’ll need to do is define your own views module. Create a blank file: mygame/web/api/views.py
+
+
A view is the python code that tells django what data to put on a page, while a template tells django how to display that data. For more in-depth information, you can read the django documentation. (docs for views, docs for templates)
+
+
The default REST API endpoints are controlled by classes in evennia/web/api/views.py - you could copy that entire file directly and use it, but we’re going to focus on changing the minimum.
+
To start, we’ll reimplement the default characters endpoint: a child view of the objects endpoint that can only access characters.
+
"""
+mygame/web/api/views.py
+
+Customized views for the REST API
+"""
+# we'll need these from django's rest framework to make our view work
+fromrest_framework.decoratorsimportaction
+fromrest_framework.responseimportResponse
+fromrest_frameworkimportstatus
+
+# this implements all the basic Evennia Object endpoint logic, so we're inheriting from it
+fromevennia.web.api.viewsimportObjectDBViewSet
+
+# and we need this to filter our character view
+fromevennia.objects.objectsimportDefaultCharacter
+
+# our own custom view
+classCharacterViewSet(ObjectDBViewSet):
+ """
+ A customized Character view that adds an inventory detail
+ """
+ queryset=DefaultCharacter.objects.all_family()
+
Now that we have a viewset of our own, we can create our own urls module and change the characters endpoint path to point to ours.
+
+
Evennia’s Game website page demonstrates how to use the urls.py module for the main website - if you haven’t gone over that page yet, now is a good time.
+
+
The API routing is more complicated than the website or webclient routing, so you need to copy the entire module from evennia into your game instead of patching on changes. Copy the file from evennia/web/api/urls.py to your folder, mygame/web/api/urls.py and open it in your editor.
+
Import your new views module, then find and update the characters path to use your own viewset.
We’ve almost got it pointing at our new view now. The last step is to add your own API urls - web.api.urls - to your web root url module. Otherwise it will continue pointing to the default API router and we’ll never see our changes.
+
Open mygame/web/urls.py in your editor and add a new path for “api/”, pointing to web.api.urls. The final file should look something like this:
+
# mygame/web/urls.py
+
+fromdjango.urlsimportpath,include
+
+# default evennia patterns
+fromevennia.web.urlsimporturlpatternsasevennia_default_urlpatterns
+
+# add patterns
+urlpatterns=[
+ # website
+ path("",include("web.website.urls")),
+ # webclient
+ path("webclient/",include("web.webclient.urls")),
+ # web admin
+ path("admin/",include("web.admin.urls")),
+
+ # the new API path
+ path("api/",include("web.api.urls")),
+]
+
+# 'urlpatterns' must be named such for django to find it.
+urlpatterns=urlpatterns+evennia_default_urlpatterns
+
+
+
Restart your evennia game - evenniareboot from the command line for a full restart of the game AND portal - and try to get /api/characters/ again. If it works exactly like before, you’re ready to move on to the next step!
Head back over to your character view class - it’s time to start adding our inventory.
+
The usual “page” in a REST API is called an endpoint and is what you typically access. e.g. /api/characters/ is the “characters” endpoint, and /api/characters/:id is the endpoint for individual characters.
+
+
The : in an API path means that it’s a variable - you don’t directly access that exact path. Instead, you’d take your character ID (e.g. 1) and use that instead: /api/characters/1
+
+
However, an endpoint can also have one or more detail views, which function like a sub-point. We’ll be adding inventory as a detail to our character endpoint, which will look like /api/characters/:id/inventory
+
With the django REST framework, adding a new detail is as simple as adding a decorated method to the view set class - the @action decorator. Since checking your inventory is just data retrieval, we’ll only want to permit the GET method, and we’re adding this action as an API detail, so our decorator will look like this:
+
@action(detail=True,methods=["get"])
+
+
+
+
There are situations where you might want a detail or endpoint that isn’t just data retrieval: for example, buy or sell on an auction-house listing. In those cases, you would use put or post instead. For further reading on what you can do with @action and ViewSets, visit the django REST framework documentation
+
+
When adding a function as a detail action, the name of our function will be the same as the detail. Since we want an inventory action we’ll define an inventory function.
+
"""
+mygame/web/api/views.py
+
+Customized views for the REST API
+"""
+# we'll need these from django's rest framework to make our view work
+fromrest_framework.decoratorsimportaction
+fromrest_framework.responseimportResponse
+fromrest_frameworkimportstatus
+
+# this implements all the basic Evennia Object endpoint logic, so we're inheriting from it
+fromevennia.web.api.viewsimportObjectDBViewSet
+
+# and we need this to filter our character view
+fromevennia.objects.objectsimportDefaultCharacter
+
+# our own custom view
+classCharacterViewSet(ObjectDBViewSet):
+ """
+ A customized Character view that adds an inventory detail
+ """
+ queryset=DefaultCharacter.objects.all_family()
+
+ # !! NEW
+ @action(detail=True,methods=["get"])
+ definventory(self,request,pk=None):
+ returnResponse("your inventory",status=status.HTTP_200_OK)
+
+
+
Get your character’s ID - it’s the same as your dbref but without the # - and then evenniareboot again. Now you should be able to call your new characters action: /api/characters/1/inventory (assuming you’re looking at character #1) and it’ll return the string “your inventory”
A simple string isn’t very useful, though. What we want is the character’s actual inventory - and for that, we need to set up our own serializer.
+
Generally speaking, a serializer turns a set of data into a specially formatted string that can be sent in a data stream - usually JSON. Django REST serializers are special classes and functions which take python objects and convert them into API-ready formats. So, just like for the viewset, django and evennia have done a lot of the heavy lifting for us already.
Instead of writing our own serializer, we’ll inherit from evennia’s pre-existing serializers and extend them for our own purpose. To do that, create a new file mygame/web/api/serializers.py and start by adding in the imports you’ll need.
+
# the base serializing library for the framework
+fromrest_frameworkimportserializers
+
+# the handy classes Evennia already prepared for us
+fromevennia.web.api.serializersimportTypeclassSerializerMixin,SimpleObjectDBSerializer
+
+# and the DefaultObject typeclass, for the necessary db model information
+fromevennia.objects.objectsimportDefaultObject
+
+
+
Next, we’ll be defining our own serializer class. Since it’s for retrieving inventory data, we’ll name it appropriately.
+
classInventorySerializer(TypeclassSerializerMixin,serializers.ModelSerializer):
+ """
+ Serializing an inventory
+ """
+
+ # these define the groups of items
+ worn=serializers.SerializerMethodField()
+ carried=serializers.SerializerMethodField()
+
+ classMeta:
+ model=DefaultObject
+ fields=[
+ "id",# required field
+ # add these to match the properties you defined
+ "worn",
+ "carried",
+ ]
+ read_only_fields=["id"]
+
+
+
The Meta class defines which fields will be used in the final serialized string. The id field is from the base ModelSerializer, but you’ll notice that the two others - worn and carried - are defined as properties to SerializerMethodField. That tells the framework to look for matching method names in the form get_X when serializing.
+
Which is why our next step is to add those methods! We defined the properties worn and carried, so the methods we’ll add are get_worn and get_carried. They’ll be static methods - that is, they don’t include self - since they don’t need to reference the serializer class itself.
+
# these methods filter the character's contents based on the `worn` attribute
+ defget_worn(character):
+ """
+ Serializes only worn objects in the target's inventory.
+ """
+ worn=[objforobjincharacter.contentsifobj.db.worn]
+ returnSimpleObjectDBSerializer(worn,many=True).data
+
+ defget_carried(character):
+ """
+ Serializes only non-worn objects in the target's inventory.
+ """
+ carried=[objforobjincharacter.contentsifnotobj.db.worn]
+ returnSimpleObjectDBSerializer(carried,many=True).data
+
+
+
For this guide, we’re assuming that whether an object is being worn or not is stored in the worn db attribute and filtering based on that attribute. This can easily be done differently to match your own game’s mechanics: filtering based on a tag, calling a custom method on your character that returns the right list, etc.
+
If you want to add in more details - grouping carried items by typing, or dividing up armor vs weapons, you’d just need to add or change the properties, fields, and methods.
+
+
Remember: worn=serializers.SerializerMethodField() is how the API knows to use get_worn, and Meta.fields is the list of fields that will actually make it into the final JSON.
+
+
Your final file should look like this:
+
# mygame/web/api/serializers.py
+
+# the base serializing library for the framework
+fromrest_frameworkimportserializers
+
+# the handy classes Evennia already prepared for us
+fromevennia.web.api.serializersimportTypeclassSerializerMixin,SimpleObjectDBSerializer
+
+# and the DefaultObject typeclass, for the necessary db model information
+fromevennia.objects.objectsimportDefaultObject
+
+classInventorySerializer(TypeclassSerializerMixin,serializers.ModelSerializer):
+ """
+ Serializing an inventory
+ """
+
+ # these define the groups of items
+ worn=serializers.SerializerMethodField()
+ carried=serializers.SerializerMethodField()
+
+ classMeta:
+ model=DefaultObject
+ fields=[
+ "id",# required field
+ # add these to match the properties you defined
+ "worn",
+ "carried",
+ ]
+ read_only_fields=["id"]
+
+ # these methods filter the character's contents based on the `worn` attribute
+ defget_worn(character):
+ """
+ Serializes only worn objects in the target's inventory.
+ """
+ worn=[objforobjincharacter.contentsifobj.db.worn]
+ returnSimpleObjectDBSerializer(worn,many=True).data
+
+ defget_carried(character):
+ """
+ Serializes only non-worn objects in the target's inventory.
+ """
+ carried=[objforobjincharacter.contentsifnotobj.db.worn]
+ returnSimpleObjectDBSerializer(carried,many=True).data
+
"""
+mygame/web/api/views.py
+
+Customized views for the REST API
+"""
+# we'll need these from django's rest framework to make our view work
+fromrest_framework.decoratorsimportaction
+fromrest_framework.responseimportResponse
+fromrest_frameworkimportstatus
+
+# this implements all the basic Evennia Object endpoint logic, so we're inheriting from it
+fromevennia.web.api.viewsimportObjectDBViewSet
+
+# and we need this to filter our character view
+fromevennia.objects.objectsimportDefaultCharacter
+
+from.serializersimportInventorySerializer# <--- NEW
+
+# our own custom view
+classCharacterViewSet(ObjectDBViewSet):
+ """
+ A customized Character view that adds an inventory detail
+ """
+ queryset=DefaultCharacter.objects.all_family()
+
+ @action(detail=True,methods=["get"])
+ definventory(self,request,pk=None):
+ returnResponse(InventorySerializer(obj).data,status=status.HTTP_200_OK)# <--- MODIFIED
+
+
+
That’ll use our new serializer to get our character’s inventory. Except… not quite.
+
Go ahead and try it: evenniareboot and then /api/characters/1/inventory like before. Instead of returning the string “your inventory”, you should get an error saying you don’t have permission. Don’t worry - that means it’s successfully referencing the new serializer. We just haven’t given it permission to access the objects yet.
Evennia comes with its own custom API permissions class, connecting the API permissions to the in-game permission hierarchy and locks system. Since we’re trying to access the object’s data now, we need to pass the has_object_permission check as well as the general permission check - and that default permission class hardcodes actions into the object permission checks.
+
Since we’ve added a new action - inventory - to our characters endpoint, we need to use our own custom permissions on our characters endpoint as well. Create one more module file: mygame/web/api/permissions.py
+
Like with the previous classes, we’ll be inheriting from the original and extending it to take advantage of all the work Evennia already does for us.
+
# mygame/web/api/permissions.py
+
+fromevennia.web.api.permissionsimportEvenniaPermission
+
+classCharacterPermission(EvenniaPermission):
+
+ defhas_object_permission(self,request,view,obj):
+ """
+ Checks object-level permissions after has_permission
+ """
+ # our new permission check
+ ifview.action=="inventory":
+ returnself.check_locks(obj,request.user,self.view_locks)
+
+ # if it's not an inventory action, run through all the default checks
+ returnsuper().has_object_permission(request,view,obj)
+
+
+
That’s the whole permission class! For our final step, we need to use it in our characters view by importing it and setting the permission_classes property.
+
Once you’ve done that, your final views.py should look like this:
+
"""
+mygame/web/api/views.py
+
+Customized views for the REST API
+"""
+# we'll need these from django's rest framework to make our view work
+fromrest_framework.decoratorsimportaction
+fromrest_framework.responseimportResponse
+fromrest_frameworkimportstatus
+
+# this implements all the basic Evennia Object endpoint logic, so we're inheriting from it
+fromevennia.web.api.viewsimportObjectDBViewSet
+
+# and we need this to filter our character view
+fromevennia.objects.objectsimportDefaultCharacter
+
+from.serializersimportInventorySerializer
+from.permissionsimportCharacterPermission# <--- NEW
+
+# our own custom view
+classCharacterViewSet(ObjectDBViewSet):
+ """
+ A customized Character view that adds an inventory detail
+ """
+ permission_classes=[CharacterPermission]# <--- NEW
+ queryset=DefaultCharacter.objects.all_family()
+
+ @action(detail=True,methods=["get"])
+ definventory(self,request,pk=None):
+ obj=self.get_object()
+ returnResponse(InventorySerializer(obj).data,status=status.HTTP_200_OK)
+
+
+
One last evenniareboot - now you should be able to get /api/characters/1/inventory and see everything your character has, neatly divided into “worn” and “carried”.
That’s it! You’ve learned how to customize your own REST endpoint for Evennia, add new endpoint details, and serialize data from your game’s objects for the REST API. With those tools, you can take any in-game data you want and make it available - or even modifiable - with the API.
+
If you want a challenge, try taking what you learned and implementing a new desc detail that will let you GET the existing character desc orPUT a new desc. (Tip: check out how evennia’s REST permissions module works, and the set_attribute methods in the default evennia REST API views.)
+
+
+
\ No newline at end of file
diff --git a/docs/1.0-dev/_sources/Howtos/Extending-the-REST-API.md.txt b/docs/1.0-dev/_sources/Howtos/Extending-the-REST-API.md.txt
new file mode 100644
index 0000000000..4f02fb0213
--- /dev/null
+++ b/docs/1.0-dev/_sources/Howtos/Extending-the-REST-API.md.txt
@@ -0,0 +1,429 @@
+# Extending the REST API
+
+> *This tutorial assumes you have a basic understanding of the `web` folder structure. If you don't, please read [the page on the website](https://www.evennia.com/docs/1.0-dev/Components/Website.html) and make sure you understand it before you come back!*
+
+For this guide, we'll be adding an `inventory` action to the `characters` endpoint, showing all objects being _worn_ and _carried_ by a character.
+
+> "worn versus carried" isn't built into core Evennia, but it's a common thing to add. This guide uses a `.db.worn` attribute to identify gear, but will explain how to reference your own mechanic too.
+
+The first thing you should do is review the [REST API](https://www.evennia.com/docs/1.0-dev/Components/Web-API.html) documentation page. It's not very long, but it covers how to turn the API on and which parts of django you should be familiar with.
+
+Once you've read that and successfully visited `/api/characters/`, it's time to get started!
+
+## Creating your own viewset
+
+The first thing you'll need to do is define your own views module. Create a blank file: `mygame/web/api/views.py`
+
+> A *view* is the python code that tells django what data to put on a page, while a *template* tells django how to display that data. For more in-depth information, you can read the django documentation. ([docs for views](https://docs.djangoproject.com/en/4.1/topics/http/views/), [docs for templates](https://docs.djangoproject.com/en/4.1/topics/templates/))
+
+The default REST API endpoints are controlled by classes in `evennia/web/api/views.py` - you could copy that entire file directly and use it, but we're going to focus on changing the minimum.
+
+To start, we'll reimplement the default `characters` endpoint: a child view of the `objects` endpoint that can only access characters.
+
+```python
+"""
+mygame/web/api/views.py
+
+Customized views for the REST API
+"""
+# we'll need these from django's rest framework to make our view work
+from rest_framework.decorators import action
+from rest_framework.response import Response
+from rest_framework import status
+
+# this implements all the basic Evennia Object endpoint logic, so we're inheriting from it
+from evennia.web.api.views import ObjectDBViewSet
+
+# and we need this to filter our character view
+from evennia.objects.objects import DefaultCharacter
+
+# our own custom view
+class CharacterViewSet(ObjectDBViewSet):
+ """
+ A customized Character view that adds an inventory detail
+ """
+ queryset = DefaultCharacter.objects.all_family()
+```
+
+## Setting up the urls
+
+Now that we have a viewset of our own, we can create our own urls module and change the `characters` endpoint path to point to ours.
+
+> Evennia's [Game website](https://www.evennia.com/docs/1.0-dev/Components/Website.html) page demonstrates how to use the `urls.py` module for the main website - if you haven't gone over that page yet, now is a good time.
+
+The API routing is more complicated than the website or webclient routing, so you need to copy the entire module from evennia into your game instead of patching on changes. Copy the file from `evennia/web/api/urls.py` to your folder, `mygame/web/api/urls.py` and open it in your editor.
+
+Import your new views module, then find and update the `characters` path to use your own viewset.
+
+```python
+# mygame/web/api/urls.py
+
+from django.urls import path
+from django.views.generic import TemplateView
+from rest_framework.schemas import get_schema_view
+
+from evennia.web.api.root import APIRootRouter
+from evennia.web.api import views
+
+from . import views as my_views # <--- NEW
+
+app_name = "api"
+
+router = APIRootRouter()
+router.trailing_slash = "/?"
+router.register(r"accounts", views.AccountDBViewSet, basename="account")
+router.register(r"objects", views.ObjectDBViewSet, basename="object")
+router.register(r"characters", my_views.CharacterViewSet, basename="character") # <--- MODIFIED
+router.register(r"exits", views.ExitViewSet, basename="exit")
+router.register(r"rooms", views.RoomViewSet, basename="room")
+router.register(r"scripts", views.ScriptDBViewSet, basename="script")
+router.register(r"helpentries", views.HelpViewSet, basename="helpentry")
+
+urlpatterns = router.urls
+
+urlpatterns += [
+ # openapi schema
+ path(
+ "openapi",
+ get_schema_view(title="Evennia API", description="Evennia OpenAPI Schema", version="1.0"),
+ name="openapi",
+ ),
+ # redoc auto-doc (based on openapi schema)
+ path(
+ "redoc/",
+ TemplateView.as_view(
+ template_name="rest_framework/redoc.html", extra_context={"schema_url": "api:openapi"}
+ ),
+ name="redoc",
+ ),
+]
+```
+
+We've almost got it pointing at our new view now. The last step is to add your own API urls - `web.api.urls` - to your web root url module. Otherwise it will continue pointing to the default API router and we'll never see our changes.
+
+Open `mygame/web/urls.py` in your editor and add a new path for "api/", pointing to `web.api.urls`. The final file should look something like this:
+
+```python
+# mygame/web/urls.py
+
+from django.urls import path, include
+
+# default evennia patterns
+from evennia.web.urls import urlpatterns as evennia_default_urlpatterns
+
+# add patterns
+urlpatterns = [
+ # website
+ path("", include("web.website.urls")),
+ # webclient
+ path("webclient/", include("web.webclient.urls")),
+ # web admin
+ path("admin/", include("web.admin.urls")),
+
+ # the new API path
+ path("api/", include("web.api.urls")),
+]
+
+# 'urlpatterns' must be named such for django to find it.
+urlpatterns = urlpatterns + evennia_default_urlpatterns
+```
+
+Restart your evennia game - `evennia reboot` from the command line for a full restart of the game AND portal - and try to get `/api/characters/` again. If it works exactly like before, you're ready to move on to the next step!
+
+## Adding a new detail
+
+Head back over to your character view class - it's time to start adding our inventory.
+
+The usual "page" in a REST API is called an *endpoint* and is what you typically access. e.g. `/api/characters/` is the "characters" endpoint, and `/api/characters/:id` is the endpoint for individual characters.
+
+> The `:` in an API path means that it's a variable - you don't directly access that exact path. Instead, you'd take your character ID (e.g. 1) and use that instead: `/api/characters/1`
+
+However, an endpoint can also have one or more *detail* views, which function like a sub-point. We'll be adding *inventory* as a detail to our character endpoint, which will look like `/api/characters/:id/inventory`
+
+With the django REST framework, adding a new detail is as simple as adding a decorated method to the view set class - the `@action` decorator. Since checking your inventory is just data retrieval, we'll only want to permit the `GET` method, and we're adding this action as an API detail, so our decorator will look like this:
+```python
+@action(detail=True, methods=["get"])
+```
+
+> There are situations where you might want a detail or endpoint that isn't just data retrieval: for example, *buy* or *sell* on an auction-house listing. In those cases, you would use *put* or *post* instead. For further reading on what you can do with `@action` and ViewSets, visit [the django REST framework documentation](https://www.django-rest-framework.org/api-guide/viewsets/)
+
+When adding a function as a detail action, the name of our function will be the same as the detail. Since we want an `inventory` action we'll define an `inventory` function.
+
+```python
+"""
+mygame/web/api/views.py
+
+Customized views for the REST API
+"""
+# we'll need these from django's rest framework to make our view work
+from rest_framework.decorators import action
+from rest_framework.response import Response
+from rest_framework import status
+
+# this implements all the basic Evennia Object endpoint logic, so we're inheriting from it
+from evennia.web.api.views import ObjectDBViewSet
+
+# and we need this to filter our character view
+from evennia.objects.objects import DefaultCharacter
+
+# our own custom view
+class CharacterViewSet(ObjectDBViewSet):
+ """
+ A customized Character view that adds an inventory detail
+ """
+ queryset = DefaultCharacter.objects.all_family()
+
+ # !! NEW
+ @action(detail=True, methods=["get"])
+ def inventory(self, request, pk=None):
+ return Response("your inventory", status=status.HTTP_200_OK )
+```
+
+Get your character's ID - it's the same as your dbref but without the # - and then `evennia reboot` again. Now you should be able to call your new characters action: `/api/characters/1/inventory` (assuming you're looking at character #1) and it'll return the string "your inventory"
+
+## Creating a Serializer
+
+A simple string isn't very useful, though. What we want is the character's actual inventory - and for that, we need to set up our own serializer.
+
+Generally speaking, a *serializer* turns a set of data into a specially formatted string that can be sent in a data stream - usually JSON. Django REST serializers are special classes and functions which take python objects and convert them into API-ready formats. So, just like for the viewset, django and evennia have done a lot of the heavy lifting for us already.
+
+> You can get a more in-depth look at django serializers on [the django REST framework serializer docs](https://www.django-rest-framework.org/api-guide/serializers/)
+
+Instead of writing our own serializer, we'll inherit from evennia's pre-existing serializers and extend them for our own purpose. To do that, create a new file `mygame/web/api/serializers.py` and start by adding in the imports you'll need.
+
+```python
+# the base serializing library for the framework
+from rest_framework import serializers
+
+# the handy classes Evennia already prepared for us
+from evennia.web.api.serializers import TypeclassSerializerMixin, SimpleObjectDBSerializer
+
+# and the DefaultObject typeclass, for the necessary db model information
+from evennia.objects.objects import DefaultObject
+```
+
+Next, we'll be defining our own serializer class. Since it's for retrieving inventory data, we'll name it appropriately.
+
+```python
+class InventorySerializer(TypeclassSerializerMixin, serializers.ModelSerializer):
+ """
+ Serializing an inventory
+ """
+
+ # these define the groups of items
+ worn = serializers.SerializerMethodField()
+ carried = serializers.SerializerMethodField()
+
+ class Meta:
+ model = DefaultObject
+ fields = [
+ "id", # required field
+ # add these to match the properties you defined
+ "worn",
+ "carried",
+ ]
+ read_only_fields = ["id"]
+```
+The `Meta` class defines which fields will be used in the final serialized string. The `id` field is from the base ModelSerializer, but you'll notice that the two others - `worn` and `carried` - are defined as properties to `SerializerMethodField`. That tells the framework to look for matching method names in the form `get_X` when serializing.
+
+Which is why our next step is to add those methods! We defined the properties `worn` and `carried`, so the methods we'll add are `get_worn` and `get_carried`. They'll be static methods - that is, they don't include `self` - since they don't need to reference the serializer class itself.
+
+```python
+ # these methods filter the character's contents based on the `worn` attribute
+ def get_worn(character):
+ """
+ Serializes only worn objects in the target's inventory.
+ """
+ worn = [obj for obj in character.contents if obj.db.worn]
+ return SimpleObjectDBSerializer(worn, many=True).data
+
+ def get_carried(character):
+ """
+ Serializes only non-worn objects in the target's inventory.
+ """
+ carried = [obj for obj in character.contents if not obj.db.worn]
+ return SimpleObjectDBSerializer(carried, many=True).data
+```
+
+For this guide, we're assuming that whether an object is being worn or not is stored in the `worn` db attribute and filtering based on that attribute. This can easily be done differently to match your own game's mechanics: filtering based on a tag, calling a custom method on your character that returns the right list, etc.
+
+If you want to add in more details - grouping carried items by typing, or dividing up armor vs weapons, you'd just need to add or change the properties, fields, and methods.
+
+> Remember: `worn = serializers.SerializerMethodField()` is how the API knows to use `get_worn`, and `Meta.fields` is the list of fields that will actually make it into the final JSON.
+
+Your final file should look like this:
+
+```python
+# mygame/web/api/serializers.py
+
+# the base serializing library for the framework
+from rest_framework import serializers
+
+# the handy classes Evennia already prepared for us
+from evennia.web.api.serializers import TypeclassSerializerMixin, SimpleObjectDBSerializer
+
+# and the DefaultObject typeclass, for the necessary db model information
+from evennia.objects.objects import DefaultObject
+
+class InventorySerializer(TypeclassSerializerMixin, serializers.ModelSerializer):
+ """
+ Serializing an inventory
+ """
+
+ # these define the groups of items
+ worn = serializers.SerializerMethodField()
+ carried = serializers.SerializerMethodField()
+
+ class Meta:
+ model = DefaultObject
+ fields = [
+ "id", # required field
+ # add these to match the properties you defined
+ "worn",
+ "carried",
+ ]
+ read_only_fields = ["id"]
+
+ # these methods filter the character's contents based on the `worn` attribute
+ def get_worn(character):
+ """
+ Serializes only worn objects in the target's inventory.
+ """
+ worn = [obj for obj in character.contents if obj.db.worn]
+ return SimpleObjectDBSerializer(worn, many=True).data
+
+ def get_carried(character):
+ """
+ Serializes only non-worn objects in the target's inventory.
+ """
+ carried = [obj for obj in character.contents if not obj.db.worn]
+ return SimpleObjectDBSerializer(carried, many=True).data
+```
+
+## Using your serializer
+
+Now let's go back to our views file, `mygame/web/api/views.py`. Add our new serializer with the rest of the imports:
+
+```python
+from .serializers import InventorySerializer
+```
+
+Then, update our `inventory` detail to use our serializer.
+```python
+ @action(detail=True, methods=["get"])
+ def inventory(self, request, pk=None):
+ obj = self.get_object()
+ return Response( InventorySerializer(obj).data, status=status.HTTP_200_OK )
+```
+
+Your views file should now look like this:
+
+```python
+"""
+mygame/web/api/views.py
+
+Customized views for the REST API
+"""
+# we'll need these from django's rest framework to make our view work
+from rest_framework.decorators import action
+from rest_framework.response import Response
+from rest_framework import status
+
+# this implements all the basic Evennia Object endpoint logic, so we're inheriting from it
+from evennia.web.api.views import ObjectDBViewSet
+
+# and we need this to filter our character view
+from evennia.objects.objects import DefaultCharacter
+
+from .serializers import InventorySerializer # <--- NEW
+
+# our own custom view
+class CharacterViewSet(ObjectDBViewSet):
+ """
+ A customized Character view that adds an inventory detail
+ """
+ queryset = DefaultCharacter.objects.all_family()
+
+ @action(detail=True, methods=["get"])
+ def inventory(self, request, pk=None):
+ return Response( InventorySerializer(obj).data, status=status.HTTP_200_OK ) # <--- MODIFIED
+```
+
+That'll use our new serializer to get our character's inventory. Except... not quite.
+
+Go ahead and try it: `evennia reboot` and then `/api/characters/1/inventory` like before. Instead of returning the string "your inventory", you should get an error saying you don't have permission. Don't worry - that means it's successfully referencing the new serializer. We just haven't given it permission to access the objects yet.
+
+## Customizing API permissions
+
+Evennia comes with its own custom API permissions class, connecting the API permissions to the in-game permission hierarchy and locks system. Since we're trying to access the object's data now, we need to pass the `has_object_permission` check as well as the general permission check - and that default permission class hardcodes actions into the object permission checks.
+
+Since we've added a new action - `inventory` - to our characters endpoint, we need to use our own custom permissions on our characters endpoint as well. Create one more module file: `mygame/web/api/permissions.py`
+
+Like with the previous classes, we'll be inheriting from the original and extending it to take advantage of all the work Evennia already does for us.
+
+```python
+# mygame/web/api/permissions.py
+
+from evennia.web.api.permissions import EvenniaPermission
+
+class CharacterPermission(EvenniaPermission):
+
+ def has_object_permission(self, request, view, obj):
+ """
+ Checks object-level permissions after has_permission
+ """
+ # our new permission check
+ if view.action == "inventory":
+ return self.check_locks(obj, request.user, self.view_locks)
+
+ # if it's not an inventory action, run through all the default checks
+ return super().has_object_permission(request, view, obj)
+```
+
+That's the whole permission class! For our final step, we need to use it in our characters view by importing it and setting the `permission_classes` property.
+
+Once you've done that, your final `views.py` should look like this:
+
+```python
+"""
+mygame/web/api/views.py
+
+Customized views for the REST API
+"""
+# we'll need these from django's rest framework to make our view work
+from rest_framework.decorators import action
+from rest_framework.response import Response
+from rest_framework import status
+
+# this implements all the basic Evennia Object endpoint logic, so we're inheriting from it
+from evennia.web.api.views import ObjectDBViewSet
+
+# and we need this to filter our character view
+from evennia.objects.objects import DefaultCharacter
+
+from .serializers import InventorySerializer
+from .permissions import CharacterPermission # <--- NEW
+
+# our own custom view
+class CharacterViewSet(ObjectDBViewSet):
+ """
+ A customized Character view that adds an inventory detail
+ """
+ permission_classes = [CharacterPermission] # <--- NEW
+ queryset = DefaultCharacter.objects.all_family()
+
+ @action(detail=True, methods=["get"])
+ def inventory(self, request, pk=None):
+ obj = self.get_object()
+ return Response( InventorySerializer(obj).data, status=status.HTTP_200_OK )
+```
+
+One last `evennia reboot` - now you should be able to get `/api/characters/1/inventory` and see everything your character has, neatly divided into "worn" and "carried".
+
+## Next Steps
+
+That's it! You've learned how to customize your own REST endpoint for Evennia, add new endpoint details, and serialize data from your game's objects for the REST API. With those tools, you can take any in-game data you want and make it available - or even modifiable - with the API.
+
+If you want a challenge, try taking what you learned and implementing a new `desc` detail that will let you `GET` the existing character desc _or_ `PUT` a new desc. (Tip: check out how evennia's REST permissions module works, and the `set_attribute` methods in the default evennia REST API views.)
+
+> For a more in-depth look at the django REST framework, you can go through [their tutorial](https://www.django-rest-framework.org/tutorial/1-serialization/) or straight to [the django REST framework API docs](https://www.django-rest-framework.org/api-guide/requests/)
\ No newline at end of file
diff --git a/docs/1.0-dev/api/evennia.commands.default.admin.html b/docs/1.0-dev/api/evennia.commands.default.admin.html
index e83e49fa66..40627cc099 100644
--- a/docs/1.0-dev/api/evennia.commands.default.admin.html
+++ b/docs/1.0-dev/api/evennia.commands.default.admin.html
@@ -317,7 +317,7 @@ to accounts respectively.
-search_index_entry = {'aliases': 'pemit remit', 'category': 'admin', 'key': 'emit', 'no_prefix': ' pemit remit', 'tags': '', 'text': '\n admin command for emitting message to multiple objects\n\n Usage:\n emit[/switches] [<obj>, <obj>, ... =] <message>\n remit [<obj>, <obj>, ... =] <message>\n pemit [<obj>, <obj>, ... =] <message>\n\n Switches:\n room - limit emits to rooms only (default)\n accounts - limit emits to accounts only\n contents - send to the contents of matched objects too\n\n Emits a message to the selected objects or to\n your immediate surroundings. If the object is a room,\n send to its contents. remit and pemit are just\n limited forms of emit, for sending to rooms and\n to accounts respectively.\n '}¶
+search_index_entry = {'aliases': 'remit pemit', 'category': 'admin', 'key': 'emit', 'no_prefix': ' remit pemit', 'tags': '', 'text': '\n admin command for emitting message to multiple objects\n\n Usage:\n emit[/switches] [<obj>, <obj>, ... =] <message>\n remit [<obj>, <obj>, ... =] <message>\n pemit [<obj>, <obj>, ... =] <message>\n\n Switches:\n room - limit emits to rooms only (default)\n accounts - limit emits to accounts only\n contents - send to the contents of matched objects too\n\n Emits a message to the selected objects or to\n your immediate surroundings. If the object is a room,\n send to its contents. remit and pemit are just\n limited forms of emit, for sending to rooms and\n to accounts respectively.\n '}¶
diff --git a/docs/1.0-dev/api/evennia.commands.default.batchprocess.html b/docs/1.0-dev/api/evennia.commands.default.batchprocess.html
index ffa29ad410..b7288acdff 100644
--- a/docs/1.0-dev/api/evennia.commands.default.batchprocess.html
+++ b/docs/1.0-dev/api/evennia.commands.default.batchprocess.html
@@ -138,7 +138,7 @@ skipping, reloading etc.
-search_index_entry = {'aliases': 'batchcommand batchcmd', 'category': 'building', 'key': 'batchcommands', 'no_prefix': ' batchcommand batchcmd', 'tags': '', 'text': '\n build from batch-command file\n\n Usage:\n batchcommands[/interactive] <python.path.to.file>\n\n Switch:\n interactive - this mode will offer more control when\n executing the batch file, like stepping,\n skipping, reloading etc.\n\n Runs batches of commands from a batch-cmd text file (*.ev).\n\n '}¶
+search_index_entry = {'aliases': 'batchcmd batchcommand', 'category': 'building', 'key': 'batchcommands', 'no_prefix': ' batchcmd batchcommand', 'tags': '', 'text': '\n build from batch-command file\n\n Usage:\n batchcommands[/interactive] <python.path.to.file>\n\n Switch:\n interactive - this mode will offer more control when\n executing the batch file, like stepping,\n skipping, reloading etc.\n\n Runs batches of commands from a batch-cmd text file (*.ev).\n\n '}¶
diff --git a/docs/1.0-dev/api/evennia.commands.default.building.html b/docs/1.0-dev/api/evennia.commands.default.building.html
index 3266b10c8d..d13a14bc98 100644
--- a/docs/1.0-dev/api/evennia.commands.default.building.html
+++ b/docs/1.0-dev/api/evennia.commands.default.building.html
@@ -592,7 +592,7 @@ You can specify the /force switch to bypass this confirmation.
@@ -633,7 +633,7 @@ You can specify the /force switch to bypass this confirmation.
-search_index_entry = {'aliases': '@del @delete', 'category': 'building', 'key': '@destroy', 'no_prefix': 'destroy del delete', 'tags': '', 'text': '\n permanently delete objects\n\n Usage:\n destroy[/switches] [obj, obj2, obj3, [dbref-dbref], ...]\n\n Switches:\n override - The destroy command will usually avoid accidentally\n destroying account objects. This switch overrides this safety.\n force - destroy without confirmation.\n Examples:\n destroy house, roof, door, 44-78\n destroy 5-10, flower, 45\n destroy/force north\n\n Destroys one or many objects. If dbrefs are used, a range to delete can be\n given, e.g. 4-10. Also the end points will be deleted. This command\n displays a confirmation before destroying, to make sure of your choice.\n You can specify the /force switch to bypass this confirmation.\n '}¶
+search_index_entry = {'aliases': '@delete @del', 'category': 'building', 'key': '@destroy', 'no_prefix': 'destroy delete del', 'tags': '', 'text': '\n permanently delete objects\n\n Usage:\n destroy[/switches] [obj, obj2, obj3, [dbref-dbref], ...]\n\n Switches:\n override - The destroy command will usually avoid accidentally\n destroying account objects. This switch overrides this safety.\n force - destroy without confirmation.\n Examples:\n destroy house, roof, door, 44-78\n destroy 5-10, flower, 45\n destroy/force north\n\n Destroys one or many objects. If dbrefs are used, a range to delete can be\n given, e.g. 4-10. Also the end points will be deleted. This command\n displays a confirmation before destroying, to make sure of your choice.\n You can specify the /force switch to bypass this confirmation.\n '}¶
-search_index_entry = {'aliases': '@swap @typeclasses @type @update @parent', 'category': 'building', 'key': '@typeclass', 'no_prefix': 'typeclass swap typeclasses type update parent', 'tags': '', 'text': "\n set or change an object's typeclass\n\n Usage:\n typeclass[/switch] <object> [= typeclass.path]\n typeclass/prototype <object> = prototype_key\n\n typeclasses or typeclass/list/show [typeclass.path]\n swap - this is a shorthand for using /force/reset flags.\n update - this is a shorthand for using the /force/reload flag.\n\n Switch:\n show, examine - display the current typeclass of object (default) or, if\n given a typeclass path, show the docstring of that typeclass.\n update - *only* re-run at_object_creation on this object\n meaning locks or other properties set later may remain.\n reset - clean out *all* the attributes and properties on the\n object - basically making this a new clean object. This will also\n reset cmdsets!\n force - change to the typeclass also if the object\n already has a typeclass of the same name.\n list - show available typeclasses. Only typeclasses in modules actually\n imported or used from somewhere in the code will show up here\n (those typeclasses are still available if you know the path)\n prototype - clean and overwrite the object with the specified\n prototype key - effectively making a whole new object.\n\n Example:\n type button = examples.red_button.RedButton\n type/prototype button=a red button\n\n If the typeclass_path is not given, the current object's typeclass is\n assumed.\n\n View or set an object's typeclass. If setting, the creation hooks of the\n new typeclass will be run on the object. If you have clashing properties on\n the old class, use /reset. By default you are protected from changing to a\n typeclass of the same name as the one you already have - use /force to\n override this protection.\n\n The given typeclass must be identified by its location using python\n dot-notation pointing to the correct module and class. If no typeclass is\n given (or a wrong typeclass is given). Errors in the path or new typeclass\n will lead to the old typeclass being kept. The location of the typeclass\n module is searched from the default typeclass directory, as defined in the\n server settings.\n\n "}¶
+search_index_entry = {'aliases': '@update @parent @type @swap @typeclasses', 'category': 'building', 'key': '@typeclass', 'no_prefix': 'typeclass update parent type swap typeclasses', 'tags': '', 'text': "\n set or change an object's typeclass\n\n Usage:\n typeclass[/switch] <object> [= typeclass.path]\n typeclass/prototype <object> = prototype_key\n\n typeclasses or typeclass/list/show [typeclass.path]\n swap - this is a shorthand for using /force/reset flags.\n update - this is a shorthand for using the /force/reload flag.\n\n Switch:\n show, examine - display the current typeclass of object (default) or, if\n given a typeclass path, show the docstring of that typeclass.\n update - *only* re-run at_object_creation on this object\n meaning locks or other properties set later may remain.\n reset - clean out *all* the attributes and properties on the\n object - basically making this a new clean object. This will also\n reset cmdsets!\n force - change to the typeclass also if the object\n already has a typeclass of the same name.\n list - show available typeclasses. Only typeclasses in modules actually\n imported or used from somewhere in the code will show up here\n (those typeclasses are still available if you know the path)\n prototype - clean and overwrite the object with the specified\n prototype key - effectively making a whole new object.\n\n Example:\n type button = examples.red_button.RedButton\n type/prototype button=a red button\n\n If the typeclass_path is not given, the current object's typeclass is\n assumed.\n\n View or set an object's typeclass. If setting, the creation hooks of the\n new typeclass will be run on the object. If you have clashing properties on\n the old class, use /reset. By default you are protected from changing to a\n typeclass of the same name as the one you already have - use /force to\n override this protection.\n\n The given typeclass must be identified by its location using python\n dot-notation pointing to the correct module and class. If no typeclass is\n given (or a wrong typeclass is given). Errors in the path or new typeclass\n will lead to the old typeclass being kept. The location of the typeclass\n module is searched from the default typeclass directory, as defined in the\n server settings.\n\n "}¶
diff --git a/docs/1.0-dev/api/evennia.commands.default.comms.html b/docs/1.0-dev/api/evennia.commands.default.comms.html
index c78ef6328f..c2f746c095 100644
--- a/docs/1.0-dev/api/evennia.commands.default.comms.html
+++ b/docs/1.0-dev/api/evennia.commands.default.comms.html
@@ -256,7 +256,7 @@ ban mychannel1,mychannel2= EvilUser : Was banned for spamming.
-search_index_entry = {'aliases': '@chan @channels', 'category': 'comms', 'key': '@channel', 'no_prefix': 'channel chan channels', 'tags': '', 'text': "\n Use and manage in-game channels.\n\n Usage:\n channel channelname <msg>\n channel channel name = <msg>\n channel (show all subscription)\n channel/all (show available channels)\n channel/alias channelname = alias[;alias...]\n channel/unalias alias\n channel/who channelname\n channel/history channelname [= index]\n channel/sub channelname [= alias[;alias...]]\n channel/unsub channelname[,channelname, ...]\n channel/mute channelname[,channelname,...]\n channel/unmute channelname[,channelname,...]\n\n channel/create channelname[;alias;alias[:typeclass]] [= description]\n channel/destroy channelname [= reason]\n channel/desc channelname = description\n channel/lock channelname = lockstring\n channel/unlock channelname = lockstring\n channel/ban channelname (list bans)\n channel/ban[/quiet] channelname[, channelname, ...] = subscribername [: reason]\n channel/unban[/quiet] channelname[, channelname, ...] = subscribername\n channel/boot[/quiet] channelname[,channelname,...] = subscribername [: reason]\n\n # subtopics\n\n ## sending\n\n Usage: channel channelname msg\n channel channel name = msg (with space in channel name)\n\n This sends a message to the channel. Note that you will rarely use this\n command like this; instead you can use the alias\n\n channelname <msg>\n channelalias <msg>\n\n For example\n\n public Hello World\n pub Hello World\n\n (this shortcut doesn't work for aliases containing spaces)\n\n See channel/alias for help on setting channel aliases.\n\n ## alias and unalias\n\n Usage: channel/alias channel = alias[;alias[;alias...]]\n channel/unalias alias\n channel - this will list your subs and aliases to each channel\n\n Set one or more personal aliases for referencing a channel. For example:\n\n channel/alias warrior's guild = warrior;wguild;warchannel;warrior guild\n\n You can now send to the channel using all of these:\n\n warrior's guild Hello\n warrior Hello\n wguild Hello\n warchannel Hello\n\n Note that this will not work if the alias has a space in it. So the\n 'warrior guild' alias must be used with the `channel` command:\n\n channel warrior guild = Hello\n\n Channel-aliases can be removed one at a time, using the '/unalias' switch.\n\n ## who\n\n Usage: channel/who channelname\n\n List the channel's subscribers. Shows who are currently offline or are\n muting the channel. Subscribers who are 'muting' will not see messages sent\n to the channel (use channel/mute to mute a channel).\n\n ## history\n\n Usage: channel/history channel [= index]\n\n This will display the last |c20|n lines of channel history. By supplying an\n index number, you will step that many lines back before viewing those 20 lines.\n\n For example:\n\n channel/history public = 35\n\n will go back 35 lines and show the previous 20 lines from that point (so\n lines -35 to -55).\n\n ## sub and unsub\n\n Usage: channel/sub channel [=alias[;alias;...]]\n channel/unsub channel\n\n This subscribes you to a channel and optionally assigns personal shortcuts\n for you to use to send to that channel (see aliases). When you unsub, all\n your personal aliases will also be removed.\n\n ## mute and unmute\n\n Usage: channel/mute channelname\n channel/unmute channelname\n\n Muting silences all output from the channel without actually\n un-subscribing. Other channel members will see that you are muted in the /who\n list. Sending a message to the channel will automatically unmute you.\n\n ## create and destroy\n\n Usage: channel/create channelname[;alias;alias[:typeclass]] [= description]\n channel/destroy channelname [= reason]\n\n Creates a new channel (or destroys one you control). You will automatically\n join the channel you create and everyone will be kicked and loose all aliases\n to a destroyed channel.\n\n ## lock and unlock\n\n Usage: channel/lock channelname = lockstring\n channel/unlock channelname = lockstring\n\n Note: this is an admin command.\n\n A lockstring is on the form locktype:lockfunc(). Channels understand three\n locktypes:\n listen - who may listen or join the channel.\n send - who may send messages to the channel\n control - who controls the channel. This is usually the one creating\n the channel.\n\n Common lockfuncs are all() and perm(). To make a channel everyone can\n listen to but only builders can talk on, use this:\n\n listen:all()\n send: perm(Builders)\n\n ## boot and ban\n\n Usage:\n channel/boot[/quiet] channelname[,channelname,...] = subscribername [: reason]\n channel/ban channelname[, channelname, ...] = subscribername [: reason]\n channel/unban channelname[, channelname, ...] = subscribername\n channel/unban channelname\n channel/ban channelname (list bans)\n\n Booting will kick a named subscriber from channel(s) temporarily. The\n 'reason' will be passed to the booted user. Unless the /quiet switch is\n used, the channel will also be informed of the action. A booted user is\n still able to re-connect, but they'll have to set up their aliases again.\n\n Banning will blacklist a user from (re)joining the provided channels. It\n will then proceed to boot them from those channels if they were connected.\n The 'reason' and `/quiet` works the same as for booting.\n\n Example:\n boot mychannel1 = EvilUser : Kicking you to cool down a bit.\n ban mychannel1,mychannel2= EvilUser : Was banned for spamming.\n\n "}¶
+search_index_entry = {'aliases': '@channels @chan', 'category': 'comms', 'key': '@channel', 'no_prefix': 'channel channels chan', 'tags': '', 'text': "\n Use and manage in-game channels.\n\n Usage:\n channel channelname <msg>\n channel channel name = <msg>\n channel (show all subscription)\n channel/all (show available channels)\n channel/alias channelname = alias[;alias...]\n channel/unalias alias\n channel/who channelname\n channel/history channelname [= index]\n channel/sub channelname [= alias[;alias...]]\n channel/unsub channelname[,channelname, ...]\n channel/mute channelname[,channelname,...]\n channel/unmute channelname[,channelname,...]\n\n channel/create channelname[;alias;alias[:typeclass]] [= description]\n channel/destroy channelname [= reason]\n channel/desc channelname = description\n channel/lock channelname = lockstring\n channel/unlock channelname = lockstring\n channel/ban channelname (list bans)\n channel/ban[/quiet] channelname[, channelname, ...] = subscribername [: reason]\n channel/unban[/quiet] channelname[, channelname, ...] = subscribername\n channel/boot[/quiet] channelname[,channelname,...] = subscribername [: reason]\n\n # subtopics\n\n ## sending\n\n Usage: channel channelname msg\n channel channel name = msg (with space in channel name)\n\n This sends a message to the channel. Note that you will rarely use this\n command like this; instead you can use the alias\n\n channelname <msg>\n channelalias <msg>\n\n For example\n\n public Hello World\n pub Hello World\n\n (this shortcut doesn't work for aliases containing spaces)\n\n See channel/alias for help on setting channel aliases.\n\n ## alias and unalias\n\n Usage: channel/alias channel = alias[;alias[;alias...]]\n channel/unalias alias\n channel - this will list your subs and aliases to each channel\n\n Set one or more personal aliases for referencing a channel. For example:\n\n channel/alias warrior's guild = warrior;wguild;warchannel;warrior guild\n\n You can now send to the channel using all of these:\n\n warrior's guild Hello\n warrior Hello\n wguild Hello\n warchannel Hello\n\n Note that this will not work if the alias has a space in it. So the\n 'warrior guild' alias must be used with the `channel` command:\n\n channel warrior guild = Hello\n\n Channel-aliases can be removed one at a time, using the '/unalias' switch.\n\n ## who\n\n Usage: channel/who channelname\n\n List the channel's subscribers. Shows who are currently offline or are\n muting the channel. Subscribers who are 'muting' will not see messages sent\n to the channel (use channel/mute to mute a channel).\n\n ## history\n\n Usage: channel/history channel [= index]\n\n This will display the last |c20|n lines of channel history. By supplying an\n index number, you will step that many lines back before viewing those 20 lines.\n\n For example:\n\n channel/history public = 35\n\n will go back 35 lines and show the previous 20 lines from that point (so\n lines -35 to -55).\n\n ## sub and unsub\n\n Usage: channel/sub channel [=alias[;alias;...]]\n channel/unsub channel\n\n This subscribes you to a channel and optionally assigns personal shortcuts\n for you to use to send to that channel (see aliases). When you unsub, all\n your personal aliases will also be removed.\n\n ## mute and unmute\n\n Usage: channel/mute channelname\n channel/unmute channelname\n\n Muting silences all output from the channel without actually\n un-subscribing. Other channel members will see that you are muted in the /who\n list. Sending a message to the channel will automatically unmute you.\n\n ## create and destroy\n\n Usage: channel/create channelname[;alias;alias[:typeclass]] [= description]\n channel/destroy channelname [= reason]\n\n Creates a new channel (or destroys one you control). You will automatically\n join the channel you create and everyone will be kicked and loose all aliases\n to a destroyed channel.\n\n ## lock and unlock\n\n Usage: channel/lock channelname = lockstring\n channel/unlock channelname = lockstring\n\n Note: this is an admin command.\n\n A lockstring is on the form locktype:lockfunc(). Channels understand three\n locktypes:\n listen - who may listen or join the channel.\n send - who may send messages to the channel\n control - who controls the channel. This is usually the one creating\n the channel.\n\n Common lockfuncs are all() and perm(). To make a channel everyone can\n listen to but only builders can talk on, use this:\n\n listen:all()\n send: perm(Builders)\n\n ## boot and ban\n\n Usage:\n channel/boot[/quiet] channelname[,channelname,...] = subscribername [: reason]\n channel/ban channelname[, channelname, ...] = subscribername [: reason]\n channel/unban channelname[, channelname, ...] = subscribername\n channel/unban channelname\n channel/ban channelname (list bans)\n\n Booting will kick a named subscriber from channel(s) temporarily. The\n 'reason' will be passed to the booted user. Unless the /quiet switch is\n used, the channel will also be informed of the action. A booted user is\n still able to re-connect, but they'll have to set up their aliases again.\n\n Banning will blacklist a user from (re)joining the provided channels. It\n will then proceed to boot them from those channels if they were connected.\n The 'reason' and `/quiet` works the same as for booting.\n\n Example:\n boot mychannel1 = EvilUser : Kicking you to cool down a bit.\n ban mychannel1,mychannel2= EvilUser : Was banned for spamming.\n\n "}¶
@@ -935,7 +935,7 @@ ban mychannel1,mychannel2= EvilUser : Was banned for spamming.
@@ -955,7 +955,7 @@ ban mychannel1,mychannel2= EvilUser : Was banned for spamming.
-search_index_entry = {'aliases': '@chan @channels', 'category': 'comms', 'key': '@channel', 'no_prefix': 'channel chan channels', 'tags': '', 'text': "\n Use and manage in-game channels.\n\n Usage:\n channel channelname <msg>\n channel channel name = <msg>\n channel (show all subscription)\n channel/all (show available channels)\n channel/alias channelname = alias[;alias...]\n channel/unalias alias\n channel/who channelname\n channel/history channelname [= index]\n channel/sub channelname [= alias[;alias...]]\n channel/unsub channelname[,channelname, ...]\n channel/mute channelname[,channelname,...]\n channel/unmute channelname[,channelname,...]\n\n channel/create channelname[;alias;alias[:typeclass]] [= description]\n channel/destroy channelname [= reason]\n channel/desc channelname = description\n channel/lock channelname = lockstring\n channel/unlock channelname = lockstring\n channel/ban channelname (list bans)\n channel/ban[/quiet] channelname[, channelname, ...] = subscribername [: reason]\n channel/unban[/quiet] channelname[, channelname, ...] = subscribername\n channel/boot[/quiet] channelname[,channelname,...] = subscribername [: reason]\n\n # subtopics\n\n ## sending\n\n Usage: channel channelname msg\n channel channel name = msg (with space in channel name)\n\n This sends a message to the channel. Note that you will rarely use this\n command like this; instead you can use the alias\n\n channelname <msg>\n channelalias <msg>\n\n For example\n\n public Hello World\n pub Hello World\n\n (this shortcut doesn't work for aliases containing spaces)\n\n See channel/alias for help on setting channel aliases.\n\n ## alias and unalias\n\n Usage: channel/alias channel = alias[;alias[;alias...]]\n channel/unalias alias\n channel - this will list your subs and aliases to each channel\n\n Set one or more personal aliases for referencing a channel. For example:\n\n channel/alias warrior's guild = warrior;wguild;warchannel;warrior guild\n\n You can now send to the channel using all of these:\n\n warrior's guild Hello\n warrior Hello\n wguild Hello\n warchannel Hello\n\n Note that this will not work if the alias has a space in it. So the\n 'warrior guild' alias must be used with the `channel` command:\n\n channel warrior guild = Hello\n\n Channel-aliases can be removed one at a time, using the '/unalias' switch.\n\n ## who\n\n Usage: channel/who channelname\n\n List the channel's subscribers. Shows who are currently offline or are\n muting the channel. Subscribers who are 'muting' will not see messages sent\n to the channel (use channel/mute to mute a channel).\n\n ## history\n\n Usage: channel/history channel [= index]\n\n This will display the last |c20|n lines of channel history. By supplying an\n index number, you will step that many lines back before viewing those 20 lines.\n\n For example:\n\n channel/history public = 35\n\n will go back 35 lines and show the previous 20 lines from that point (so\n lines -35 to -55).\n\n ## sub and unsub\n\n Usage: channel/sub channel [=alias[;alias;...]]\n channel/unsub channel\n\n This subscribes you to a channel and optionally assigns personal shortcuts\n for you to use to send to that channel (see aliases). When you unsub, all\n your personal aliases will also be removed.\n\n ## mute and unmute\n\n Usage: channel/mute channelname\n channel/unmute channelname\n\n Muting silences all output from the channel without actually\n un-subscribing. Other channel members will see that you are muted in the /who\n list. Sending a message to the channel will automatically unmute you.\n\n ## create and destroy\n\n Usage: channel/create channelname[;alias;alias[:typeclass]] [= description]\n channel/destroy channelname [= reason]\n\n Creates a new channel (or destroys one you control). You will automatically\n join the channel you create and everyone will be kicked and loose all aliases\n to a destroyed channel.\n\n ## lock and unlock\n\n Usage: channel/lock channelname = lockstring\n channel/unlock channelname = lockstring\n\n Note: this is an admin command.\n\n A lockstring is on the form locktype:lockfunc(). Channels understand three\n locktypes:\n listen - who may listen or join the channel.\n send - who may send messages to the channel\n control - who controls the channel. This is usually the one creating\n the channel.\n\n Common lockfuncs are all() and perm(). To make a channel everyone can\n listen to but only builders can talk on, use this:\n\n listen:all()\n send: perm(Builders)\n\n ## boot and ban\n\n Usage:\n channel/boot[/quiet] channelname[,channelname,...] = subscribername [: reason]\n channel/ban channelname[, channelname, ...] = subscribername [: reason]\n channel/unban channelname[, channelname, ...] = subscribername\n channel/unban channelname\n channel/ban channelname (list bans)\n\n Booting will kick a named subscriber from channel(s) temporarily. The\n 'reason' will be passed to the booted user. Unless the /quiet switch is\n used, the channel will also be informed of the action. A booted user is\n still able to re-connect, but they'll have to set up their aliases again.\n\n Banning will blacklist a user from (re)joining the provided channels. It\n will then proceed to boot them from those channels if they were connected.\n The 'reason' and `/quiet` works the same as for booting.\n\n Example:\n boot mychannel1 = EvilUser : Kicking you to cool down a bit.\n ban mychannel1,mychannel2= EvilUser : Was banned for spamming.\n\n "}¶
+search_index_entry = {'aliases': '@channels @chan', 'category': 'comms', 'key': '@channel', 'no_prefix': 'channel channels chan', 'tags': '', 'text': "\n Use and manage in-game channels.\n\n Usage:\n channel channelname <msg>\n channel channel name = <msg>\n channel (show all subscription)\n channel/all (show available channels)\n channel/alias channelname = alias[;alias...]\n channel/unalias alias\n channel/who channelname\n channel/history channelname [= index]\n channel/sub channelname [= alias[;alias...]]\n channel/unsub channelname[,channelname, ...]\n channel/mute channelname[,channelname,...]\n channel/unmute channelname[,channelname,...]\n\n channel/create channelname[;alias;alias[:typeclass]] [= description]\n channel/destroy channelname [= reason]\n channel/desc channelname = description\n channel/lock channelname = lockstring\n channel/unlock channelname = lockstring\n channel/ban channelname (list bans)\n channel/ban[/quiet] channelname[, channelname, ...] = subscribername [: reason]\n channel/unban[/quiet] channelname[, channelname, ...] = subscribername\n channel/boot[/quiet] channelname[,channelname,...] = subscribername [: reason]\n\n # subtopics\n\n ## sending\n\n Usage: channel channelname msg\n channel channel name = msg (with space in channel name)\n\n This sends a message to the channel. Note that you will rarely use this\n command like this; instead you can use the alias\n\n channelname <msg>\n channelalias <msg>\n\n For example\n\n public Hello World\n pub Hello World\n\n (this shortcut doesn't work for aliases containing spaces)\n\n See channel/alias for help on setting channel aliases.\n\n ## alias and unalias\n\n Usage: channel/alias channel = alias[;alias[;alias...]]\n channel/unalias alias\n channel - this will list your subs and aliases to each channel\n\n Set one or more personal aliases for referencing a channel. For example:\n\n channel/alias warrior's guild = warrior;wguild;warchannel;warrior guild\n\n You can now send to the channel using all of these:\n\n warrior's guild Hello\n warrior Hello\n wguild Hello\n warchannel Hello\n\n Note that this will not work if the alias has a space in it. So the\n 'warrior guild' alias must be used with the `channel` command:\n\n channel warrior guild = Hello\n\n Channel-aliases can be removed one at a time, using the '/unalias' switch.\n\n ## who\n\n Usage: channel/who channelname\n\n List the channel's subscribers. Shows who are currently offline or are\n muting the channel. Subscribers who are 'muting' will not see messages sent\n to the channel (use channel/mute to mute a channel).\n\n ## history\n\n Usage: channel/history channel [= index]\n\n This will display the last |c20|n lines of channel history. By supplying an\n index number, you will step that many lines back before viewing those 20 lines.\n\n For example:\n\n channel/history public = 35\n\n will go back 35 lines and show the previous 20 lines from that point (so\n lines -35 to -55).\n\n ## sub and unsub\n\n Usage: channel/sub channel [=alias[;alias;...]]\n channel/unsub channel\n\n This subscribes you to a channel and optionally assigns personal shortcuts\n for you to use to send to that channel (see aliases). When you unsub, all\n your personal aliases will also be removed.\n\n ## mute and unmute\n\n Usage: channel/mute channelname\n channel/unmute channelname\n\n Muting silences all output from the channel without actually\n un-subscribing. Other channel members will see that you are muted in the /who\n list. Sending a message to the channel will automatically unmute you.\n\n ## create and destroy\n\n Usage: channel/create channelname[;alias;alias[:typeclass]] [= description]\n channel/destroy channelname [= reason]\n\n Creates a new channel (or destroys one you control). You will automatically\n join the channel you create and everyone will be kicked and loose all aliases\n to a destroyed channel.\n\n ## lock and unlock\n\n Usage: channel/lock channelname = lockstring\n channel/unlock channelname = lockstring\n\n Note: this is an admin command.\n\n A lockstring is on the form locktype:lockfunc(). Channels understand three\n locktypes:\n listen - who may listen or join the channel.\n send - who may send messages to the channel\n control - who controls the channel. This is usually the one creating\n the channel.\n\n Common lockfuncs are all() and perm(). To make a channel everyone can\n listen to but only builders can talk on, use this:\n\n listen:all()\n send: perm(Builders)\n\n ## boot and ban\n\n Usage:\n channel/boot[/quiet] channelname[,channelname,...] = subscribername [: reason]\n channel/ban channelname[, channelname, ...] = subscribername [: reason]\n channel/unban channelname[, channelname, ...] = subscribername\n channel/unban channelname\n channel/ban channelname (list bans)\n\n Booting will kick a named subscriber from channel(s) temporarily. The\n 'reason' will be passed to the booted user. Unless the /quiet switch is\n used, the channel will also be informed of the action. A booted user is\n still able to re-connect, but they'll have to set up their aliases again.\n\n Banning will blacklist a user from (re)joining the provided channels. It\n will then proceed to boot them from those channels if they were connected.\n The 'reason' and `/quiet` works the same as for booting.\n\n Example:\n boot mychannel1 = EvilUser : Kicking you to cool down a bit.\n ban mychannel1,mychannel2= EvilUser : Was banned for spamming.\n\n "}¶
diff --git a/docs/1.0-dev/api/evennia.commands.default.general.html b/docs/1.0-dev/api/evennia.commands.default.general.html
index 7ee6533e6a..310f75866f 100644
--- a/docs/1.0-dev/api/evennia.commands.default.general.html
+++ b/docs/1.0-dev/api/evennia.commands.default.general.html
@@ -268,7 +268,7 @@ for everyone to use, you need build privileges and the alias command.
@@ -300,7 +300,7 @@ for everyone to use, you need build privileges and the alias command.
-search_index_entry = {'aliases': 'nicks nickname', 'category': 'general', 'key': 'nick', 'no_prefix': ' nicks nickname', 'tags': '', 'text': '\n define a personal alias/nick by defining a string to\n match and replace it with another on the fly\n\n Usage:\n nick[/switches] <string> [= [replacement_string]]\n nick[/switches] <template> = <replacement_template>\n nick/delete <string> or number\n nicks\n\n Switches:\n inputline - replace on the inputline (default)\n object - replace on object-lookup\n account - replace on account-lookup\n list - show all defined aliases (also "nicks" works)\n delete - remove nick by index in /list\n clearall - clear all nicks\n\n Examples:\n nick hi = say Hello, I\'m Sarah!\n nick/object tom = the tall man\n nick build $1 $2 = create/drop $1;$2\n nick tell $1 $2=page $1=$2\n nick tm?$1=page tallman=$1\n nick tm\\=$1=page tallman=$1\n\n A \'nick\' is a personal string replacement. Use $1, $2, ... to catch arguments.\n Put the last $-marker without an ending space to catch all remaining text. You\n can also use unix-glob matching for the left-hand side <string>:\n\n * - matches everything\n ? - matches 0 or 1 single characters\n [abcd] - matches these chars in any order\n [!abcd] - matches everything not among these chars\n \\= - escape literal \'=\' you want in your <string>\n\n Note that no objects are actually renamed or changed by this command - your nicks\n are only available to you. If you want to permanently add keywords to an object\n for everyone to use, you need build privileges and the alias command.\n\n '}¶
+search_index_entry = {'aliases': 'nickname nicks', 'category': 'general', 'key': 'nick', 'no_prefix': ' nickname nicks', 'tags': '', 'text': '\n define a personal alias/nick by defining a string to\n match and replace it with another on the fly\n\n Usage:\n nick[/switches] <string> [= [replacement_string]]\n nick[/switches] <template> = <replacement_template>\n nick/delete <string> or number\n nicks\n\n Switches:\n inputline - replace on the inputline (default)\n object - replace on object-lookup\n account - replace on account-lookup\n list - show all defined aliases (also "nicks" works)\n delete - remove nick by index in /list\n clearall - clear all nicks\n\n Examples:\n nick hi = say Hello, I\'m Sarah!\n nick/object tom = the tall man\n nick build $1 $2 = create/drop $1;$2\n nick tell $1 $2=page $1=$2\n nick tm?$1=page tallman=$1\n nick tm\\=$1=page tallman=$1\n\n A \'nick\' is a personal string replacement. Use $1, $2, ... to catch arguments.\n Put the last $-marker without an ending space to catch all remaining text. You\n can also use unix-glob matching for the left-hand side <string>:\n\n * - matches everything\n ? - matches 0 or 1 single characters\n [abcd] - matches these chars in any order\n [!abcd] - matches everything not among these chars\n \\= - escape literal \'=\' you want in your <string>\n\n Note that no objects are actually renamed or changed by this command - your nicks\n are only available to you. If you want to permanently add keywords to an object\n for everyone to use, you need build privileges and the alias command.\n\n '}¶
@@ -773,7 +773,7 @@ which permission groups you are a member of.
@@ -804,7 +804,7 @@ which permission groups you are a member of.
-search_index_entry = {'aliases': 'hierarchy groups', 'category': 'general', 'key': 'access', 'no_prefix': ' hierarchy groups', 'tags': '', 'text': '\n show your current game access\n\n Usage:\n access\n\n This command shows you the permission hierarchy and\n which permission groups you are a member of.\n '}¶
+search_index_entry = {'aliases': 'groups hierarchy', 'category': 'general', 'key': 'access', 'no_prefix': ' groups hierarchy', 'tags': '', 'text': '\n show your current game access\n\n Usage:\n access\n\n This command shows you the permission hierarchy and\n which permission groups you are a member of.\n '}¶
diff --git a/docs/1.0-dev/api/evennia.commands.default.tests.html b/docs/1.0-dev/api/evennia.commands.default.tests.html
index 10278ee12a..a5c460b192 100644
--- a/docs/1.0-dev/api/evennia.commands.default.tests.html
+++ b/docs/1.0-dev/api/evennia.commands.default.tests.html
@@ -902,7 +902,7 @@ main test suite started with
Test the batch processor.
-red_button = <module 'evennia.contrib.tutorials.red_button.red_button' from '/tmp/tmp5j5rru7h/a15a8ae42888846c44f33882b196663ac29d8cb3/evennia/contrib/tutorials/red_button/red_button.py'>¶
+red_button = <module 'evennia.contrib.tutorials.red_button.red_button' from '/tmp/tmprbs5hb8l/260e96e503b60ff031dae46edba66e3040274ab5/evennia/contrib/tutorials/red_button/red_button.py'>¶
@@ -157,7 +157,7 @@ there is no object yet before the account has logged in)
-search_index_entry = {'aliases': 'con conn co', 'category': 'general', 'key': 'connect', 'no_prefix': ' con conn co', 'tags': '', 'text': '\n connect to the game\n\n Usage (at login screen):\n connect accountname password\n connect "account name" "pass word"\n\n Use the create command to first create an account before logging in.\n\n If you have spaces in your name, enclose it in double quotes.\n '}¶
+search_index_entry = {'aliases': 'con co conn', 'category': 'general', 'key': 'connect', 'no_prefix': ' con co conn', 'tags': '', 'text': '\n connect to the game\n\n Usage (at login screen):\n connect accountname password\n connect "account name" "pass word"\n\n Use the create command to first create an account before logging in.\n\n If you have spaces in your name, enclose it in double quotes.\n '}¶
@@ -286,7 +286,7 @@ All it does is display the connect screen.
@@ -312,7 +312,7 @@ All it does is display the connect screen.
-search_index_entry = {'aliases': 'l look', 'category': 'general', 'key': '__unloggedin_look_command', 'no_prefix': ' l look', 'tags': '', 'text': '\n look when in unlogged-in state\n\n Usage:\n look\n\n This is an unconnected version of the look command for simplicity.\n\n This is called by the server and kicks everything in gear.\n All it does is display the connect screen.\n '}¶
+search_index_entry = {'aliases': 'look l', 'category': 'general', 'key': '__unloggedin_look_command', 'no_prefix': ' look l', 'tags': '', 'text': '\n look when in unlogged-in state\n\n Usage:\n look\n\n This is an unconnected version of the look command for simplicity.\n\n This is called by the server and kicks everything in gear.\n All it does is display the connect screen.\n '}¶
@@ -335,7 +335,7 @@ for simplicity. It shows a pane of info.
@@ -361,7 +361,7 @@ for simplicity. It shows a pane of info.
-search_index_entry = {'aliases': 'h ?', 'category': 'general', 'key': 'help', 'no_prefix': ' h ?', 'tags': '', 'text': '\n get help when in unconnected-in state\n\n Usage:\n help\n\n This is an unconnected version of the help command,\n for simplicity. It shows a pane of info.\n '}¶
+search_index_entry = {'aliases': '? h', 'category': 'general', 'key': 'help', 'no_prefix': ' ? h', 'tags': '', 'text': '\n get help when in unconnected-in state\n\n Usage:\n help\n\n This is an unconnected version of the help command,\n for simplicity. It shows a pane of info.\n '}¶
diff --git a/docs/1.0-dev/api/evennia.contrib.base_systems.email_login.email_login.html b/docs/1.0-dev/api/evennia.contrib.base_systems.email_login.email_login.html
index 9288c091b2..d553d6c828 100644
--- a/docs/1.0-dev/api/evennia.contrib.base_systems.email_login.email_login.html
+++ b/docs/1.0-dev/api/evennia.contrib.base_systems.email_login.email_login.html
@@ -139,7 +139,7 @@ the module given by settings.CONNECTION_SCREEN_MODULE.
@@ -169,7 +169,7 @@ there is no object yet before the account has logged in)
-search_index_entry = {'aliases': 'con conn co', 'category': 'general', 'key': 'connect', 'no_prefix': ' con conn co', 'tags': '', 'text': '\n Connect to the game.\n\n Usage (at login screen):\n connect <email> <password>\n\n Use the create command to first create an account before logging in.\n '}¶
+search_index_entry = {'aliases': 'con co conn', 'category': 'general', 'key': 'connect', 'no_prefix': ' con co conn', 'tags': '', 'text': '\n Connect to the game.\n\n Usage (at login screen):\n connect <email> <password>\n\n Use the create command to first create an account before logging in.\n '}¶
@@ -291,7 +291,7 @@ All it does is display the connect screen.
@@ -317,7 +317,7 @@ All it does is display the connect screen.
-search_index_entry = {'aliases': 'l look', 'category': 'general', 'key': '__unloggedin_look_command', 'no_prefix': ' l look', 'tags': '', 'text': '\n This is an unconnected version of the `look` command for simplicity.\n\n This is called by the server and kicks everything in gear.\n All it does is display the connect screen.\n '}¶
+search_index_entry = {'aliases': 'look l', 'category': 'general', 'key': '__unloggedin_look_command', 'no_prefix': ' look l', 'tags': '', 'text': '\n This is an unconnected version of the `look` command for simplicity.\n\n This is called by the server and kicks everything in gear.\n All it does is display the connect screen.\n '}¶
@@ -335,7 +335,7 @@ for simplicity. It shows a pane of info.
@@ -361,7 +361,7 @@ for simplicity. It shows a pane of info.
-search_index_entry = {'aliases': 'h ?', 'category': 'general', 'key': 'help', 'no_prefix': ' h ?', 'tags': '', 'text': '\n This is an unconnected version of the help command,\n for simplicity. It shows a pane of info.\n '}¶
+search_index_entry = {'aliases': '? h', 'category': 'general', 'key': 'help', 'no_prefix': ' ? h', 'tags': '', 'text': '\n This is an unconnected version of the help command,\n for simplicity. It shows a pane of info.\n '}¶
@@ -191,7 +191,7 @@ aliases to an already joined channel.
-search_index_entry = {'aliases': 'chanalias aliaschan', 'category': 'comms', 'key': 'addcom', 'no_prefix': ' chanalias aliaschan', 'tags': '', 'text': '\n Add a channel alias and/or subscribe to a channel\n\n Usage:\n addcom [alias=] <channel>\n\n Joins a given channel. If alias is given, this will allow you to\n refer to the channel by this alias rather than the full channel\n name. Subsequent calls of this command can be used to add multiple\n aliases to an already joined channel.\n '}¶
+search_index_entry = {'aliases': 'aliaschan chanalias', 'category': 'comms', 'key': 'addcom', 'no_prefix': ' aliaschan chanalias', 'tags': '', 'text': '\n Add a channel alias and/or subscribe to a channel\n\n Usage:\n addcom [alias=] <channel>\n\n Joins a given channel. If alias is given, this will allow you to\n refer to the channel by this alias rather than the full channel\n name. Subsequent calls of this command can be used to add multiple\n aliases to an already joined channel.\n '}¶
diff --git a/docs/1.0-dev/api/evennia.contrib.full_systems.evscaperoom.commands.html b/docs/1.0-dev/api/evennia.contrib.full_systems.evscaperoom.commands.html
index c5d086c488..1f1cd9d151 100644
--- a/docs/1.0-dev/api/evennia.contrib.full_systems.evscaperoom.commands.html
+++ b/docs/1.0-dev/api/evennia.contrib.full_systems.evscaperoom.commands.html
@@ -211,7 +211,7 @@ the operation will be general or on the room.
-search_index_entry = {'aliases': 'abort quit chicken out q', 'category': 'evscaperoom', 'key': 'give up', 'no_prefix': ' abort quit chicken out q', 'tags': '', 'text': '\n Give up\n\n Usage:\n give up\n\n Abandons your attempts at escaping and of ever winning the pie-eating contest.\n\n '}¶
+search_index_entry = {'aliases': 'q chicken out abort quit', 'category': 'evscaperoom', 'key': 'give up', 'no_prefix': ' q chicken out abort quit', 'tags': '', 'text': '\n Give up\n\n Usage:\n give up\n\n Abandons your attempts at escaping and of ever winning the pie-eating contest.\n\n '}¶
-search_index_entry = {'aliases': 'e ex unfocus examine', 'category': 'evscaperoom', 'key': 'focus', 'no_prefix': ' e ex unfocus examine', 'tags': '', 'text': '\n Focus your attention on a target.\n\n Usage:\n focus <obj>\n\n Once focusing on an object, use look to get more information about how it\n looks and what actions is available.\n\n '}¶
+search_index_entry = {'aliases': 'examine unfocus e ex', 'category': 'evscaperoom', 'key': 'focus', 'no_prefix': ' examine unfocus e ex', 'tags': '', 'text': '\n Focus your attention on a target.\n\n Usage:\n focus <obj>\n\n Once focusing on an object, use look to get more information about how it\n looks and what actions is available.\n\n '}¶
@@ -649,7 +649,7 @@ to all the variables defined therein.
-search_index_entry = {'aliases': '@open @dig', 'category': 'general', 'key': 'open', 'no_prefix': ' open dig', 'tags': '', 'text': '\n Interact with an object in focus.\n\n Usage:\n <action> [arg]\n\n '}¶
+search_index_entry = {'aliases': '@dig @open', 'category': 'general', 'key': 'open', 'no_prefix': ' dig open', 'tags': '', 'text': '\n Interact with an object in focus.\n\n Usage:\n <action> [arg]\n\n '}¶
diff --git a/docs/1.0-dev/api/evennia.contrib.game_systems.barter.barter.html b/docs/1.0-dev/api/evennia.contrib.game_systems.barter.barter.html
index 2be2efb51c..60f08ffd36 100644
--- a/docs/1.0-dev/api/evennia.contrib.game_systems.barter.barter.html
+++ b/docs/1.0-dev/api/evennia.contrib.game_systems.barter.barter.html
@@ -745,7 +745,7 @@ try to influence the other part in the deal.
@@ -771,7 +771,7 @@ try to influence the other part in the deal.
-search_index_entry = {'aliases': 'deal offers', 'category': 'trading', 'key': 'status', 'no_prefix': ' deal offers', 'tags': '', 'text': "\n show a list of the current deal\n\n Usage:\n status\n deal\n offers\n\n Shows the currently suggested offers on each sides of the deal. To\n accept the current deal, use the 'accept' command. Use 'offer' to\n change your deal. You might also want to use 'say', 'emote' etc to\n try to influence the other part in the deal.\n "}¶
+search_index_entry = {'aliases': 'offers deal', 'category': 'trading', 'key': 'status', 'no_prefix': ' offers deal', 'tags': '', 'text': "\n show a list of the current deal\n\n Usage:\n status\n deal\n offers\n\n Shows the currently suggested offers on each sides of the deal. To\n accept the current deal, use the 'accept' command. Use 'offer' to\n change your deal. You might also want to use 'say', 'emote' etc to\n try to influence the other part in the deal.\n "}¶
diff --git a/docs/1.0-dev/api/evennia.contrib.rpg.dice.dice.html b/docs/1.0-dev/api/evennia.contrib.rpg.dice.dice.html
index c485bd9dc5..f934d57a9e 100644
--- a/docs/1.0-dev/api/evennia.contrib.rpg.dice.dice.html
+++ b/docs/1.0-dev/api/evennia.contrib.rpg.dice.dice.html
@@ -305,7 +305,7 @@ everyone but the person rolling.
@@ -331,7 +331,7 @@ everyone but the person rolling.
-search_index_entry = {'aliases': '@dice roll', 'category': 'general', 'key': 'dice', 'no_prefix': ' dice roll', 'tags': '', 'text': "\n roll dice\n\n Usage:\n dice[/switch] <nr>d<sides> [modifier] [success condition]\n\n Switch:\n hidden - tell the room the roll is being done, but don't show the result\n secret - don't inform the room about neither roll nor result\n\n Examples:\n dice 3d6 + 4\n dice 1d100 - 2 < 50\n\n This will roll the given number of dice with given sides and modifiers.\n So e.g. 2d6 + 3 means to 'roll a 6-sided die 2 times and add the result,\n then add 3 to the total'.\n Accepted modifiers are +, -, * and /.\n A success condition is given as normal Python conditionals\n (<,>,<=,>=,==,!=). So e.g. 2d6 + 3 > 10 means that the roll will succeed\n only if the final result is above 8. If a success condition is given, the\n outcome (pass/fail) will be echoed along with how much it succeeded/failed\n with. The hidden/secret switches will hide all or parts of the roll from\n everyone but the person rolling.\n "}¶
+search_index_entry = {'aliases': 'roll @dice', 'category': 'general', 'key': 'dice', 'no_prefix': ' roll dice', 'tags': '', 'text': "\n roll dice\n\n Usage:\n dice[/switch] <nr>d<sides> [modifier] [success condition]\n\n Switch:\n hidden - tell the room the roll is being done, but don't show the result\n secret - don't inform the room about neither roll nor result\n\n Examples:\n dice 3d6 + 4\n dice 1d100 - 2 < 50\n\n This will roll the given number of dice with given sides and modifiers.\n So e.g. 2d6 + 3 means to 'roll a 6-sided die 2 times and add the result,\n then add 3 to the total'.\n Accepted modifiers are +, -, * and /.\n A success condition is given as normal Python conditionals\n (<,>,<=,>=,==,!=). So e.g. 2d6 + 3 > 10 means that the roll will succeed\n only if the final result is above 8. If a success condition is given, the\n outcome (pass/fail) will be echoed along with how much it succeeded/failed\n with. The hidden/secret switches will hide all or parts of the roll from\n everyone but the person rolling.\n "}¶
diff --git a/docs/1.0-dev/api/evennia.contrib.tutorials.red_button.red_button.html b/docs/1.0-dev/api/evennia.contrib.tutorials.red_button.red_button.html
index bf65957899..aa9fadecc2 100644
--- a/docs/1.0-dev/api/evennia.contrib.tutorials.red_button.red_button.html
+++ b/docs/1.0-dev/api/evennia.contrib.tutorials.red_button.red_button.html
@@ -506,7 +506,7 @@ be mutually exclusive.
-search_index_entry = {'aliases': 'ex get listen feel l examine', 'category': 'general', 'key': 'look', 'no_prefix': ' ex get listen feel l examine', 'tags': '', 'text': "\n Looking around in darkness\n\n Usage:\n look <obj>\n\n ... not that there's much to see in the dark.\n\n "}¶
+search_index_entry = {'aliases': 'get feel l listen examine ex', 'category': 'general', 'key': 'look', 'no_prefix': ' get feel l listen examine ex', 'tags': '', 'text': "\n Looking around in darkness\n\n Usage:\n look <obj>\n\n ... not that there's much to see in the dark.\n\n "}¶
diff --git a/docs/1.0-dev/api/evennia.contrib.tutorials.tutorial_world.objects.html b/docs/1.0-dev/api/evennia.contrib.tutorials.tutorial_world.objects.html
index 962c0ed952..e5306603e9 100644
--- a/docs/1.0-dev/api/evennia.contrib.tutorials.tutorial_world.objects.html
+++ b/docs/1.0-dev/api/evennia.contrib.tutorials.tutorial_world.objects.html
@@ -556,7 +556,7 @@ shift green root up/down
@@ -805,7 +805,7 @@ parry - forgoes your attack but will make you harder to hit on next
-search_index_entry = {'aliases': 'defend thrust fight hit parry stab bash chop slash pierce kill', 'category': 'tutorialworld', 'key': 'attack', 'no_prefix': ' defend thrust fight hit parry stab bash chop slash pierce kill', 'tags': '', 'text': '\n Attack the enemy. Commands:\n\n stab <enemy>\n slash <enemy>\n parry\n\n stab - (thrust) makes a lot of damage but is harder to hit with.\n slash - is easier to land, but does not make as much damage.\n parry - forgoes your attack but will make you harder to hit on next\n enemy attack.\n\n '}¶
+search_index_entry = {'aliases': 'chop fight kill hit bash thrust defend pierce parry slash stab', 'category': 'tutorialworld', 'key': 'attack', 'no_prefix': ' chop fight kill hit bash thrust defend pierce parry slash stab', 'tags': '', 'text': '\n Attack the enemy. Commands:\n\n stab <enemy>\n slash <enemy>\n parry\n\n stab - (thrust) makes a lot of damage but is harder to hit with.\n slash - is easier to land, but does not make as much damage.\n parry - forgoes your attack but will make you harder to hit on next\n enemy attack.\n\n '}¶
diff --git a/docs/1.0-dev/api/evennia.contrib.tutorials.tutorial_world.rooms.html b/docs/1.0-dev/api/evennia.contrib.tutorials.tutorial_world.rooms.html
index b1814d672d..2ff08e2790 100644
--- a/docs/1.0-dev/api/evennia.contrib.tutorials.tutorial_world.rooms.html
+++ b/docs/1.0-dev/api/evennia.contrib.tutorials.tutorial_world.rooms.html
@@ -816,7 +816,7 @@ if they fall off the bridge.
-search_index_entry = {'aliases': 'yes abort a y __nomatch_command no n', 'category': 'general', 'key': '__noinput_command', 'no_prefix': ' yes abort a y __nomatch_command no n', 'tags': '', 'text': '\n Handle a prompt for yes or no. Press [return] for the default choice.\n\n '}¶
+search_index_entry = {'aliases': 'yes __nomatch_command a y no abort n', 'category': 'general', 'key': '__noinput_command', 'no_prefix': ' yes __nomatch_command a y no abort n', 'tags': '', 'text': '\n Handle a prompt for yes or no. Press [return] for the default choice.\n\n '}¶
diff --git a/docs/1.0-dev/api/evennia.utils.evmore.html b/docs/1.0-dev/api/evennia.utils.evmore.html
index 322c7dc7db..08885e2b08 100644
--- a/docs/1.0-dev/api/evennia.utils.evmore.html
+++ b/docs/1.0-dev/api/evennia.utils.evmore.html
@@ -137,7 +137,7 @@ the caller.msg() construct every time the page is updated.
@@ -163,7 +163,7 @@ the caller.msg() construct every time the page is updated.
-search_index_entry = {'aliases': 'e quit p t abort a previous end next q top n', 'category': 'general', 'key': '__noinput_command', 'no_prefix': ' e quit p t abort a previous end next q top n', 'tags': '', 'text': '\n Manipulate the text paging. Catch no-input with aliases.\n '}¶
+search_index_entry = {'aliases': 'p q top t previous a end e quit next abort n', 'category': 'general', 'key': '__noinput_command', 'no_prefix': ' p q top t previous a end e quit next abort n', 'tags': '', 'text': '\n Manipulate the text paging. Catch no-input with aliases.\n '}¶