From c7ec3dfad38389a2cdd4c68a1eb71834c4f70361 Mon Sep 17 00:00:00 2001 From: Griatch Date: Sat, 26 Nov 2022 15:37:02 +0100 Subject: [PATCH] Refactoring Concept/Component documentation. Still not done --- docs/pylib/update_default_cmd_index.py | 2 +- docs/source/Coding/Coding-Overview.md | 1 + docs/source/Coding/Default-Command-Syntax.md | 24 ++ docs/source/{Concepts => Coding}/Soft-Code.md | 0 docs/source/Coding/Unit-Testing.md | 2 +- docs/source/Components/Accounts.md | 2 +- .../Bootstrap-Components-and-Utilities.md | 80 ----- docs/source/Components/Components-Overview.md | 3 +- docs/source/Components/Default-Commands.md | 2 +- docs/source/Components/FuncParser.md | 8 +- docs/source/Components/Objects.md | 5 - docs/source/Components/Outputfuncs.md | 3 - docs/source/Components/Permissions.md | 82 ++--- docs/source/Components/Portal-And-Server.md | 14 +- docs/source/Components/Sessions.md | 2 +- docs/source/Components/Web-API.md | 22 +- .../Components/Web-Bootstrap-Framework.md | 164 +++++++++ docs/source/Components/Website.md | 144 ++------ docs/source/Concepts/Async-Process.md | 235 +++++-------- docs/source/Concepts/Banning.md | 68 ++-- docs/source/Concepts/Bootstrap-&-Evennia.md | 101 ------ docs/source/Concepts/Building-Permissions.md | 72 ---- ...iver.md => Change-Message-Per-Receiver.md} | 63 +--- docs/source/Concepts/Colors.md | 11 +- docs/source/Concepts/Concepts-Overview.md | 47 +-- .../Concepts/{Guest-Logins.md => Guests.md} | 0 docs/source/Concepts/Inline-Functions.md | 22 ++ .../Concepts/Inline-Tags-and-Functions.md | 18 - docs/source/Concepts/Messagepath.md | 323 +++++++++--------- .../Concepts/{New-Models.md => Models.md} | 0 docs/source/Concepts/OOB.md | 159 ++++----- .../{Custom-Protocols.md => Protocols.md} | 0 .../source/Concepts/Tags-Parsed-By-Evennia.md | 27 ++ .../Concepts/Using-MUX-as-a-Standard.md | 12 - docs/source/Concepts/Web-Features.md | 133 -------- docs/source/Evennia-Introduction.md | 2 +- .../Beginner-Tutorial-Building-Quickstart.md | 2 +- ...ner-Tutorial-Planning-The-Tutorial-Game.md | 2 +- .../Evennia-for-roleplaying-sessions.md | 157 +++------ .../Howtos/Implementing-a-game-rule-system.md | 2 +- docs/source/Howtos/Web-Changing-Webpage.md | 2 +- .../source/Howtos/Web-Character-Generation.md | 2 +- 42 files changed, 745 insertions(+), 1275 deletions(-) create mode 100644 docs/source/Coding/Default-Command-Syntax.md rename docs/source/{Concepts => Coding}/Soft-Code.md (100%) delete mode 100644 docs/source/Components/Bootstrap-Components-and-Utilities.md delete mode 100644 docs/source/Components/Outputfuncs.md create mode 100644 docs/source/Components/Web-Bootstrap-Framework.md delete mode 100644 docs/source/Concepts/Bootstrap-&-Evennia.md delete mode 100644 docs/source/Concepts/Building-Permissions.md rename docs/source/Concepts/{Change-Messages-Per-Receiver.md => Change-Message-Per-Receiver.md} (74%) rename docs/source/Concepts/{Guest-Logins.md => Guests.md} (100%) create mode 100644 docs/source/Concepts/Inline-Functions.md delete mode 100644 docs/source/Concepts/Inline-Tags-and-Functions.md rename docs/source/Concepts/{New-Models.md => Models.md} (100%) rename docs/source/Concepts/{Custom-Protocols.md => Protocols.md} (100%) create mode 100644 docs/source/Concepts/Tags-Parsed-By-Evennia.md delete mode 100644 docs/source/Concepts/Using-MUX-as-a-Standard.md delete mode 100644 docs/source/Concepts/Web-Features.md diff --git a/docs/pylib/update_default_cmd_index.py b/docs/pylib/update_default_cmd_index.py index 25733b0eb4..5dd2c44836 100644 --- a/docs/pylib/update_default_cmd_index.py +++ b/docs/pylib/update_default_cmd_index.py @@ -19,7 +19,7 @@ PAGE = """ # Default Commands The full set of default Evennia commands currently contains {ncommands} commands in {nfiles} source -files. Our policy for adding default commands is outlined [here](Using-MUX-as-a-Standard). The +files. Our policy for adding default commands is outlined [here](Default-Command-Syntax). The [Commands](Commands) documentation explains how Commands work as well as how to make new or customize existing ones. diff --git a/docs/source/Coding/Coding-Overview.md b/docs/source/Coding/Coding-Overview.md index 8b436f9ca7..b6a2436309 100644 --- a/docs/source/Coding/Coding-Overview.md +++ b/docs/source/Coding/Coding-Overview.md @@ -9,6 +9,7 @@ See also the [Beginner Tutorial](../Howtos/Beginner-Tutorial/Beginner-Tutorial-O :maxdepth: 2 Evennia-Code-Style.md +Default-Command-Syntax.md Version-Control.md Debugging.md Unit-Testing.md diff --git a/docs/source/Coding/Default-Command-Syntax.md b/docs/source/Coding/Default-Command-Syntax.md new file mode 100644 index 0000000000..10f1e4ccbf --- /dev/null +++ b/docs/source/Coding/Default-Command-Syntax.md @@ -0,0 +1,24 @@ +# Default Command Syntax + + +Evennia allows for any command syntax. + +If you like the way DikuMUDs, LPMuds or MOOs handle things, you could emulate that with Evennia. If you are ambitious you could even design a whole new style, perfectly fitting your own dreams of the ideal game. See the [Command](../Components/Commands.md) documentation for how to do this. + +We do offer a default however. The default Evennia setup tends to *resemble* [MUX2](https://www.tinymux.org/), and its cousins [PennMUSH](https://www.pennmush.org), [TinyMUSH](https://github.com/TinyMUSH/TinyMUSH/wiki), and [RhostMUSH](http://www.rhostmush.com/): + +``` +command[/switches] object [= options] +``` + +While the reason for this similarity is partly historical, these codebases offer very mature feature sets for administration and building. + +Evennia is *not* a MUX system though. It works very differently in many ways. For example, Evennia +deliberately lacks an online softcode language (a policy explained on our [softcode policy page](./Soft-Code.md)). Evennia also does not shy from using its own syntax when deemed appropriate: the +MUX syntax has grown organically over a long time and is, frankly, rather arcane in places. All in +all the default command syntax should at most be referred to as "MUX-like" or "MUX-inspired". + +```{toctree} +:hidden: +Soft-Code +``` \ No newline at end of file diff --git a/docs/source/Concepts/Soft-Code.md b/docs/source/Coding/Soft-Code.md similarity index 100% rename from docs/source/Concepts/Soft-Code.md rename to docs/source/Coding/Soft-Code.md diff --git a/docs/source/Coding/Unit-Testing.md b/docs/source/Coding/Unit-Testing.md index 833ac5d3b0..0b8bf95c58 100644 --- a/docs/source/Coding/Unit-Testing.md +++ b/docs/source/Coding/Unit-Testing.md @@ -221,7 +221,7 @@ test coverage and which does not. All help is appreciated! ### Unit testing contribs with custom models A special case is if you were to create a contribution to go to the `evennia/contrib` folder that -uses its [own database models](../Concepts/New-Models.md). The problem with this is that Evennia (and Django) will +uses its [own database models](../Concepts/Models.md). The problem with this is that Evennia (and Django) will only recognize models in `settings.INSTALLED_APPS`. If a user wants to use your contrib, they will be required to add your models to their settings file. But since contribs are optional you cannot add the model to Evennia's central `settings_default.py` file - this would always create your diff --git a/docs/source/Components/Accounts.md b/docs/source/Components/Accounts.md index 02865b38e2..d97b0cb2ce 100644 --- a/docs/source/Components/Accounts.md +++ b/docs/source/Components/Accounts.md @@ -2,7 +2,7 @@ ``` ┌──────┐ │ ┌───────┐ ┌───────┐ ┌──────┐ -│Client├─┼──►│Session├───►│Account├──►│Object│ +│Client├─┼──►│Session├───►│Account├──►│Object│ └──────┘ │ └───────┘ └───────┘ └──────┘ ^ ``` diff --git a/docs/source/Components/Bootstrap-Components-and-Utilities.md b/docs/source/Components/Bootstrap-Components-and-Utilities.md deleted file mode 100644 index 69b2a89b9b..0000000000 --- a/docs/source/Components/Bootstrap-Components-and-Utilities.md +++ /dev/null @@ -1,80 +0,0 @@ -# Bootstrap Components and Utilities - -Bootstrap provides many utilities and components you can use when customizing Evennia's web -presence. We'll go over a few examples here that you might find useful. -> Please take a look at either [the basic web tutorial](../Howtos/Beginner-Tutorial/Part5/Add-a-simple-new-web-page.md) or ->[the web character view tutorial](../Howtos/Web-Character-View-Tutorial.md) -> to get a feel for how to add pages to Evennia's website to test these examples. - -## General Styling -Bootstrap provides base styles for your site. These can be customized through CSS, but the default -styles are intended to provide a consistent, clean look for sites. - -### Color -Most elements can be styled with default colors. [Take a look at the documentation](https://getbootstrap.com/docs/4.0/utilities/colors/) to learn more about these colors -- suffice to say, adding a class of text-* or bg-*, for instance, text-primary, sets the text color -or background color. - -### Borders -Simply adding a class of 'border' to an element adds a border to the element. For more in-depth -info, please [read the documentation on borders.](https://getbootstrap.com/docs/4.0/utilities/borders/). -``` - -``` -You can also easily round corners just by adding a class. -``` - -``` - -### Spacing -Bootstrap provides classes to easily add responsive margin and padding. Most of the time, you might -like to add margins or padding through CSS itself - however these classes are used in the default -Evennia site. [Take a look at the docs](https://getbootstrap.com/docs/4.0/utilities/spacing/) to -learn more. - -*** -## Components - -### Buttons -[Buttons](https://getbootstrap.com/docs/4.0/components/buttons/) in Bootstrap are very easy to use - -button styling can be added to ` - - - -``` -### Cards -[Cards](https://getbootstrap.com/docs/4.0/components/card/) provide a container for other elements -that stands out from the rest of the page. The "Accounts", "Recently Connected", and "Database -Stats" on the default webpage are all in cards. Cards provide quite a bit of formatting options - -the following is a simple example, but read the documentation or look at the site's source for more. -``` -
-
-

Card title

-
Card subtitle
-

Fancy, isn't it?

- Card link -
-
-``` - -### Jumbotron -[Jumbotrons](https://getbootstrap.com/docs/4.0/components/jumbotron/) are useful for featuring an -image or tagline for your game. They can flow with the rest of your content or take up the full -width of the page - Evennia's base site uses the former. -``` -
-
-

Full Width Jumbotron

-

Look at the source of the default Evennia page for a regular Jumbotron

-
-
-``` - -### Forms -[Forms](https://getbootstrap.com/docs/4.0/components/forms/) are highly customizable with Bootstrap. -For a more in-depth look at how to use forms and their styles in your own Evennia site, please read -over [the web character gen tutorial.](../Howtos/Web-Character-Generation.md) \ No newline at end of file diff --git a/docs/source/Components/Components-Overview.md b/docs/source/Components/Components-Overview.md index 0f42a4a34a..b7dc5b0897 100644 --- a/docs/source/Components/Components-Overview.md +++ b/docs/source/Components/Components-Overview.md @@ -41,7 +41,6 @@ Batch-Processors.md Batch-Code-Processor.md Batch-Command-Processor.md Inputfuncs.md -Outputfuncs.md ``` @@ -77,5 +76,5 @@ Webclient.md Web-Admin.md Webserver.md Web-API.md -Bootstrap-Components-and-Utilities.md +Web-Bootstrap-Framework.md ``` \ No newline at end of file diff --git a/docs/source/Components/Default-Commands.md b/docs/source/Components/Default-Commands.md index 040368c6de..e930330d9f 100644 --- a/docs/source/Components/Default-Commands.md +++ b/docs/source/Components/Default-Commands.md @@ -1,7 +1,7 @@ # Default Commands The full set of default Evennia commands currently contains 88 commands in 9 source -files. Our policy for adding default commands is outlined [here](../Concepts/Using-MUX-as-a-Standard.md). The +files. Our policy for adding default commands is outlined [here](../Coding/Default-Command-Syntax.md). The [Commands](./Commands.md) documentation explains how Commands work as well as how to make new or customize existing ones. diff --git a/docs/source/Components/FuncParser.md b/docs/source/Components/FuncParser.md index eddba3fe5a..78913b742e 100644 --- a/docs/source/Components/FuncParser.md +++ b/docs/source/Components/FuncParser.md @@ -1,10 +1,6 @@ # The Inline Function Parser -The [FuncParser](evennia.utils.funcparser.FuncParser) extracts and executes -'inline functions' -embedded in a string on the form `$funcname(args, kwargs)`. Under the hood, this will -lead to a call to a Python function you control. The inline function call will be replaced by -the return from the function. +The [FuncParser](evennia.utils.funcparser.FuncParser) extracts and executes 'inline functions' embedded in a string on the form `$funcname(args, kwargs)`. Under the hood, this will lead to a call to a Python function you control. The inline function call will be replaced by the return from the function. ```python from evennia.utils.funcparser import FuncParser @@ -378,7 +374,7 @@ The `caller` is required, it's the the object to do the access-check for. The `a These are used to implement actor-stance emoting. They are used by the [DefaultObject.msg_contents](evennia.objects.objects.DefaultObject.msg_contents) method by default. You can read a lot more about this on the page -[Change messages per receiver](../Concepts/Change-Messages-Per-Receiver.md). +[Change messages per receiver](../Concepts/Change-Message-Per-Receiver.md). On the parser side, all these inline functions require extra kwargs be passed into the parser (done by `msg_contents` by default): diff --git a/docs/source/Components/Objects.md b/docs/source/Components/Objects.md index 3a030ca03f..352169e86d 100644 --- a/docs/source/Components/Objects.md +++ b/docs/source/Components/Objects.md @@ -17,23 +17,18 @@ An Evennia Object is, by definition, a Python class that includes [evennia.obje ┌────────────┐ Evennia│ │ObjectParent│ library│ └──────▲─────┘ - │ │ ┌─────────────┐ │ ┌──────┐ │ │DefaultObject◄────────────────────┼────┤Object├──────┤ └──────▲──────┘ │ └──────┘ │ - │ │ │ │ ┌────────────────┐ │ ┌─────────┐ │ ├────────┤DefaultCharacter◄─┼────┤Character├───┤ │ └────────────────┘ │ └─────────┘ │ - │ │ │ │ ┌────────────────┐ │ ┌────┐ │ ├────────┤DefaultRoom ◄─┼────┤Room├────────┤ │ └────────────────┘ │ └────┘ │ - │ │ │ │ ┌────────────────┐ │ ┌────┐ │ └────────┤DefaultExit ◄─┼────┤Exit├────────┘ └────────────────┘ │ └────┘ - │ │Game-dir ``` diff --git a/docs/source/Components/Outputfuncs.md b/docs/source/Components/Outputfuncs.md deleted file mode 100644 index f0eca56e4b..0000000000 --- a/docs/source/Components/Outputfuncs.md +++ /dev/null @@ -1,3 +0,0 @@ -# Outputfuncs - -TODO. For now info about outputfuncs are found in [OOB](../Concepts/OOB.md). \ No newline at end of file diff --git a/docs/source/Components/Permissions.md b/docs/source/Components/Permissions.md index 84d4028a77..83a1b97d15 100644 --- a/docs/source/Components/Permissions.md +++ b/docs/source/Components/Permissions.md @@ -1,38 +1,43 @@ # Permissions -A *permission* is simply a text string stored in the handler `permissions` on `Objects` -and `Accounts`. Think of it as a specialized sort of [Tag](./Tags.md) - one specifically dedicated -to access checking. They are thus often tightly coupled to [Locks](./Locks.md). -Permission strings are not case-sensitive, so "Builder" is the same as "builder" -etc. +A *permission* is simply a text string stored in the handler `permissions` on `Objects` and `Accounts`. Think of it as a specialized sort of [Tag](./Tags.md) - one specifically dedicated to access checking. They are thus often tightly coupled to [Locks](./Locks.md). Permission strings are not case-sensitive, so "Builder" is the same as "builder" etc. -Permissions are used as a convenient way to structure access levels and -hierarchies. It is set by the `perm` command and checked by the -`PermissionHandler.check` method as well as by the specially the `perm()` and -`pperm()` [lock functions](./Locks.md). +Permissions are used as a convenient way to structure access levels and hierarchies. It is set by the `perm` command and checked by the `PermissionHandler.check` method as well as by the specially the `perm()` and `pperm()` [lock functions](./Locks.md). -All new accounts are given a default set of permissions defined by -`settings.PERMISSION_ACCOUNT_DEFAULT`. +All new accounts are given a default set of permissions defined by `settings.PERMISSION_ACCOUNT_DEFAULT`. + +## The super user + +There are strictly speaking two types of users in Evennia, the *super user* and everyone else. The +superuser is the first user you create, object `#1`. This is the all-powerful server-owner account. +Technically the superuser not only has access to everything, it *bypasses* the permission checks +entirely. + +This makes the superuser impossible to lock out, but makes it unsuitable to actually play- +test the game's locks and restrictions with (see `quell` below). Usually there is no need to have +but one superuser. ## Managing Permissions In-game, you use the `perm` command to add and remove permissions -j - perm/account Tommy = Builders - perm/account/del Tommy = Builders -Note the use of the `/account` switch. It means you assign the permission to the -[Accounts](./Accounts.md) Tommy instead of any [Character](./Objects.md) that also -happens to be named "Tommy". + > perm/account Tommy = Builders + > perm/account/del Tommy = Builders -There can be reasons for putting permissions on Objects (especially NPCS), but -for granting powers to players, you should usually put the permission on the -`Account` - this guarantees that they are kept, *regardless* -of which Character they are currently puppeting. This is especially important to -remember when assigning permissions from the *hierarchy tree* (see below), as an -Account's permissions will overrule that of its character. So to be sure to -avoid confusion you should generally put hierarchy permissions on the Account, -not on their Characters (but see also [quelling](#quelling)). +Note the use of the `/account` switch. It means you assign the permission to the [Accounts](./Accounts.md) Tommy instead of any [Character](./Objects.md) that also happens to be named "Tommy". If you don't want to use `/account`, you can also prefix the name with `*` to indicate an Account is sought: + + > perm *Tommy = Builders + +There can be reasons for putting permissions on Objects (especially NPCS), but for granting powers to players, you should usually put the permission on the `Account` - this guarantees that they are kept, *regardless* of which Character they are currently puppeting. + +This is especially important to remember when assigning permissions from the *hierarchy tree* (see below), as an Account's permissions will overrule that of its character. So to be sure to avoid confusion you should generally put hierarchy permissions on the Account, not on their Characters/puppets. + +If you _do_ want to start using the permissions on your _puppet_, you use `quell` + + > quell + > unquell + +This drops to the permissions on the puppeted object, and then back to your Account-permissions again. Quelling is useful if you want to try something "as" someone else. It's also useful for superusers since this makes them susceptible to locks (so they can test things). In code, you add/remove Permissions via the `PermissionHandler`, which sits on all typeclassed entities as the property `.permissions`: @@ -44,7 +49,6 @@ typeclassed entities as the property `.permissions`: obj.permissions.remove("Blacksmith") ``` - ## The permission hierarchy Selected permission strings can be organized in a *permission hierarchy* by editing the tuple @@ -57,35 +61,19 @@ Selected permission strings can be organized in a *permission hierarchy* by edit Admin # can administrate accounts Developer # like superuser but affected by locks (highest) -(Besides being case-insensitive, hierarchical permissions also understand the -plural form, so you could use `Developers` and `Developer` interchangeably). +(Besides being case-insensitive, hierarchical permissions also understand the plural form, so you could use `Developers` and `Developer` interchangeably). -> There is also a `Guest` level below `Player` that is only active if `settings.GUEST_ENABLED` is -set. The Guest is is never part of `settings.PERMISSION_HIERARCHY`. +> There is also a `Guest` level below `Player` that is only active if `settings.GUEST_ENABLED` is set. The Guest is is never part of `settings.PERMISSION_HIERARCHY`. -When checking a hierarchical permission (using one of the methods to follow), -you will pass checks for your level and all *below* you. That is, even if the -check explicitly checks for "Builder" level access, you will actually pass if you have -one of "Builder", "Admin" or "Developer". By contrast, if you check for a -non-hierarchical permission, like "Blacksmith" you *must* have exactly -that permission to pass. +When checking a hierarchical permission (using one of the methods to follow), you will pass checks for your level and all *below* you. That is, even if the check explicitly checks for "Builder" level access, you will actually pass if you have one of "Builder", "Admin" or "Developer". By contrast, if you check for a non-hierarchical permission, like "Blacksmith" you *must* have exactly that permission to pass. ## Checking permissions -It's important to note that you check for the permission of a *puppeted* -[Object](./Objects.md) (like a Character), the check will always first use the -permissions of any `Account` connected to that Object before checking for -permissions on the Object. In the case of hierarchical permissions (Admins, -Builders etc), the Account permission will always be used (this stops an Account -from escalating their permission by puppeting a high-level Character). If the -permission looked for is not in the hierarchy, an exact match is required, first -on the Account and if not found there (or if no Account is connected), then on -the Object itself. +It's important to note that you check for the permission of a *puppeted* [Object](./Objects.md) (like a Character), the check will always first use the permissions of any `Account` connected to that Object before checking for permissions on the Object. In the case of hierarchical permissions (Admins, Builders etc), the Account permission will always be used (this stops an Account from escalating their permission by puppeting a high-level Character). If the permission looked for is not in the hierarchy, an exact match is required, first on the Account and if not found there (or if no Account is connected), then on the Object itself. ### Checking with obj.permissions.check() -The simplest way to check if an entity has a permission is to check its -_PermissionHandler_, stored as `.permissions` on all typeclassed entities. +The simplest way to check if an entity has a permission is to check its _PermissionHandler_, stored as `.permissions` on all typeclassed entities. if obj.permissions.check("Builder"): # allow builder to do stuff diff --git a/docs/source/Components/Portal-And-Server.md b/docs/source/Components/Portal-And-Server.md index c3527e5409..6203e5a34b 100644 --- a/docs/source/Components/Portal-And-Server.md +++ b/docs/source/Components/Portal-And-Server.md @@ -13,15 +13,15 @@ The effect of this is that you can fully `reload` the Server and have players st ``` Internet│ ┌──────────┐ ┌─┐ ┌─┐ ┌─────────┐ │ │Portal │ │S│ ┌───┐ │S│ │Server │ - P │ │ ├─┤e├───┤AMP├───┤e├─┤ │ - l ──┼──┤ Telnet │ │s│ │ │ │s│ │ │ - a │ │ Webclient├─┤s├───┤ ├───┤s├─┤ Game │ - y ──┼──┤ SSH │ │i│ │ │ │i│ │ Database│ - e │ │ ... ├─┤o├───┤ ├───┤o├─┤ │ - r ──┼──┤ │ │n│ │ │ │n│ │ │ + P │ │ │ │e│ │AMP│ │e│ │ │ + l ──┼──┤ Telnet ├─┤s├───┤ ├───┤s├─┤ │ + a │ │ Webclient│ │s│ │ │ │s│ │ Game │ + y ──┼──┤ SSH ├─┤i├───┤ ├───┤i├─┤ Database│ + e │ │ ... │ │o│ │ │ │o│ │ │ + r ──┼──┤ ├─┤n├───┤ ├───┤n├─┤ │ s │ │ │ │s│ └───┘ │s│ │ │ │ └──────────┘ └─┘ └─┘ └─────────┘ - │Evennia + │Evennia ``` The Server and Portal are glued together via an AMP (Asynchronous Messaging Protocol) connection. This allows the two programs to communicate seamlessly on the same machine. \ No newline at end of file diff --git a/docs/source/Components/Sessions.md b/docs/source/Components/Sessions.md index c9b931269b..99152ade39 100644 --- a/docs/source/Components/Sessions.md +++ b/docs/source/Components/Sessions.md @@ -186,7 +186,7 @@ server reboot (assuming the Portal is not stopped at the same time, obviously). Both the Portal and Server each have a *sessionhandler* to manage the connections. These handlers are global entities contain all methods for relaying data across the AMP bridge. All types of Sessions hold a reference to their respective Sessionhandler (the property is called -`sessionhandler`) so they can relay data. See [protocols](../Concepts/Custom-Protocols.md) for more info +`sessionhandler`) so they can relay data. See [protocols](../Concepts/Protocols.md) for more info on building new protocols. To get all Sessions in the game (i.e. all currently connected clients), you access the server-side diff --git a/docs/source/Components/Web-API.md b/docs/source/Components/Web-API.md index 0a38073c3e..da4b70e31c 100644 --- a/docs/source/Components/Web-API.md +++ b/docs/source/Components/Web-API.md @@ -1,11 +1,7 @@ # Evennia REST API Evennia makes its database accessible via a REST API found on -[http://localhost:4001/api](http://localhost:4001/api) if running locally with -default setup. The API allows you to retrieve, edit and create resources from -outside the game, for example with your own custom client or game editor. - -While you can view and learn about the api in the web browser, it is really +[http://localhost:4001/api](http://localhost:4001/api) if running locally with default setup. The API allows you to retrieve, edit and create resources from outside the game, for example with your own custom client or game editor. While you can view and learn about the api in the web browser, it is really meant to be accessed in code, by other programs. The API is using [Django Rest Framework][drf]. This automates the process @@ -55,16 +51,10 @@ To list a specific type of object: "previous": null, "results" : [{"db_key": "A rusty longsword", "id": 57, "db_location": 213, ...}]} -In the above example, it now displays the objects inside the "results" array, -while it has a "count" value for the number of total objects, and "next" and -"previous" links for the next and previous page, if any. This is called -[pagination][pagination], and the link displays "limit" and "offset" as query -parameters that can be added to the url to control the output. +In the above example, it now displays the objects inside the "results" array, while it has a "count" value for the number of total objects, and "next" and "previous" links for the next and previous page, if any. This is called [pagination][pagination], and the link displays "limit" and "offset" as query parameters that can be added to the url to control the output. -Other query parameters can be defined as [filters][filters] which allow you to -further narrow the results. For example, to only get accounts with developer -permissions: +Other query parameters can be defined as [filters][filters] which allow you to further narrow the results. For example, to only get accounts with developer permissions: >>> response = requests.get("https://www.mygame.com/api/accounts/?permission=developer", auth=("MyUserName", "password123")) @@ -83,11 +73,7 @@ Now suppose that you want to use the API to create an [Object](./Objects.md): {"db_key": "A shiny sword", "id": 214, "db_location": None, ...} -Here we made a HTTP POST request to the `/api/objects` endpoint with the `db_key` -we wanted. We got back info for the newly created object. You can now make -another request with PUT (replace everything) or PATCH (replace only what you -provide). By providing the id to the endpoint (`/api/objects/214`), -we make sure to update the right sword: +Here we made a HTTP POST request to the `/api/objects` endpoint with the `db_key` we wanted. We got back info for the newly created object. You can now make another request with PUT (replace everything) or PATCH (replace only what you provide). By providing the id to the endpoint (`/api/objects/214`), we make sure to update the right sword: >>> data = {"db_key": "An even SHINIER sword", "db_location": 50} >>> response = requests.put("https://www.mygame.com/api/objects/214", diff --git a/docs/source/Components/Web-Bootstrap-Framework.md b/docs/source/Components/Web-Bootstrap-Framework.md new file mode 100644 index 0000000000..131f8c703b --- /dev/null +++ b/docs/source/Components/Web-Bootstrap-Framework.md @@ -0,0 +1,164 @@ +# Bootstrap frontend framework + +Evennia's default web page uses a framework called [Bootstrap](https://getbootstrap.com/). This framework is in use across the internet - you'll probably start to recognize its influence once you learn some of the common design patterns. This switch is great for web developers, perhaps like yourself, because instead of wondering about setting up different grid systems or what custom class another designer used, we have a base, a bootstrap, to work from. Bootstrap is responsive by default, and comes with some default styles that Evennia has lightly overrode to keep some of the same colors and styles you're used to from the previous design. + +e, a brief overview of Bootstrap follows. For more in-depth info, please +read [the documentation](https://getbootstrap.com/docs/4.0/getting-started/introduction/). + +## Grid system + +Other than the basic styling Bootstrap includes, it also includes [a built in layout and grid system](https://getbootstrap.com/docs/4.0/layout/overview/). + +### The container + +The first part of the grid system is [the container](https://getbootstrap.com/docs/4.0/layout/overview/#containers). + +The container is meant to hold all your page content. Bootstrap provides two types: fixed-width and +full-width. Fixed-width containers take up a certain max-width of the page - they're useful for limiting the width on Desktop or Tablet platforms, instead of making the content span the width of the page. + +``` +
+ +
+``` +Full width containers take up the maximum width available to them - they'll span across a wide- +screen desktop or a smaller screen phone, edge-to-edge. +``` +
+ +
+``` + +### The grid + +The second part of the layout system is [the grid](https://getbootstrap.com/docs/4.0/layout/grid/). + +This is the bread-and-butter of the layout of Bootstrap - it allows you to change the size of elements depending on the size of the screen, without writing any media queries. We'll briefly go over it - to learn more, please read the docs or look at the source code for Evennia's home page in your browser. + +> Important! Grid elements should be in a .container or .container-fluid. This will center the +contents of your site. + +Bootstrap's grid system allows you to create rows and columns by applying classes based on breakpoints. The default breakpoints are extra small, small, medium, large, and extra-large. If you'd like to know more about these breakpoints, please [take a look at the documentation for +them.](https://getbootstrap.com/docs/4.0/layout/overview/#responsive-breakpoints) + +To use the grid system, first create a container for your content, then add your rows and columns like so: +``` +
+
+
+ 1 of 3 +
+
+ 2 of 3 +
+
+ 3 of 3 +
+
+
+``` +This layout would create three equal-width columns. + +To specify your sizes - for instance, Evennia's default site has three columns on desktop and +tablet, but reflows to single-column on smaller screens. Try it out! +``` +
+
+
+ 1 of 4 +
+
+ 2 of 4 +
+
+ 3 of 4 +
+
+ 4 of 4 +
+
+
+``` +This layout would be 4 columns on large screens, 2 columns on medium screens, and 1 column on +anything smaller. + +To learn more about Bootstrap's grid, please [take a look at the +docs](https://getbootstrap.com/docs/4.0/layout/grid/) +I +## General Styling elements + +Bootstrap provides base styles for your site. These can be customized through CSS, but the default +styles are intended to provide a consistent, clean look for sites. + +### Color +Most elements can be styled with default colors. [Take a look at the documentation](https://getbootstrap.com/docs/4.0/utilities/colors/) to learn more about these colors +- suffice to say, adding a class of text-* or bg-*, for instance, text-primary, sets the text color +or background color. + +### Borders + +Simply adding a class of 'border' to an element adds a border to the element. For more in-depth +info, please [read the documentation on borders.](https://getbootstrap.com/docs/4.0/utilities/borders/). +``` + +``` +You can also easily round corners just by adding a class. +``` + +``` + +### Spacing +Bootstrap provides classes to easily add responsive margin and padding. Most of the time, you might like to add margins or padding through CSS itself - however these classes are used in the default Evennia site. [Take a look at the docs](https://getbootstrap.com/docs/4.0/utilities/spacing/) to +learn more. + +### Buttons + +[Buttons](https://getbootstrap.com/docs/4.0/components/buttons/) in Bootstrap are very easy to use - button styling can be added to ` + + + +``` + +### Cards + +[Cards](https://getbootstrap.com/docs/4.0/components/card/) provide a container for other elements +that stands out from the rest of the page. The "Accounts", "Recently Connected", and "Database +Stats" on the default webpage are all in cards. Cards provide quite a bit of formatting options - +the following is a simple example, but read the documentation or look at the site's source for more. +``` +
+
+

Card title

+
Card subtitle
+

Fancy, isn't it?

+ Card link +
+
+``` + +### Jumbotron + +[Jumbotrons](https://getbootstrap.com/docs/4.0/components/jumbotron/) are useful for featuring an +image or tagline for your game. They can flow with the rest of your content or take up the full +width of the page - Evennia's base site uses the former. +``` +
+
+

Full Width Jumbotron

+

Look at the source of the default Evennia page for a regular Jumbotron

+
+
+``` + +### Forms + +[Forms](https://getbootstrap.com/docs/4.0/components/forms/) are highly customizable with Bootstrap. +For a more in-depth look at how to use forms and their styles in your own Evennia site, please read +over [the web character gen tutorial.](../Howtos/Web-Character-Generation.md) + +## Further reading + +Bootstrap also provides a huge amount of utilities, as well as styling and content elements. To learn more about them, please [read the Bootstrap docs](https://getbootstrap.com/docs/4.0/getting- started/introduction/) or read one of our other web tutorials. \ No newline at end of file diff --git a/docs/source/Components/Website.md b/docs/source/Components/Website.md index 03c345ca0e..c87677467a 100644 --- a/docs/source/Components/Website.md +++ b/docs/source/Components/Website.md @@ -1,20 +1,10 @@ # Game website -When Evennia starts it will also start a [Webserver](./Webserver.md) as part of the -[Server](./Portal-And-Server.md) process. This uses [Django](https://docs.djangoproject.com) -to present a simple but functional default game website. With the default setup, -open your browser to [localhost:4001](http://localhost:4001) or [127.0.0.1:4001](http://127.0.0.1:4001) -to see it. +When Evennia starts it will also start a [Webserver](./Webserver.md) as part of the [Server](./Portal-And-Server.md) process. This uses [Django](https://docs.djangoproject.com) to present a simple but functional default game website. With the default setup, open your browser to [localhost:4001](http://localhost:4001) or [127.0.0.1:4001](http://127.0.0.1:4001) to see it. -The website allows existing players to log in using an account-name and -password they previously used to register with the game. If a user logs in with -the [Webclient](./Webclient.md) they will also log into the website and vice-versa. -So if you are logged into the website, opening the webclient will automatically -log you into the game as that account. +The website allows existing players to log in using an account-name and password they previously used to register with the game. If a user logs in with the [Webclient](./Webclient.md) they will also log into the website and vice-versa. So if you are logged into the website, opening the webclient will automatically log you into the game as that account. -The default website shows a "Welcome!" page with a few links to useful -resources. It also shows some statistics about how many players are currently -connected. +The default website shows a "Welcome!" page with a few links to useful resources. It also shows some statistics about how many players are currently connected. In the top menu you can find - _Home_ - Get back to front page. @@ -41,10 +31,7 @@ In the top menu you can find ## Modifying the default Website -You can modify and override all aspects of the web site from your game dir. -You'll mostly be doing so in your settings file -(`mygame/server/conf/settings.py` and in the gamedir's `web/folder` -(`mygame/web/` if your game folder is `mygame/`). +You can modify and override all aspects of the web site from your game dir. You'll mostly be doing so in your settings file (`mygame/server/conf/settings.py` and in the gamedir's `web/folder` (`mygame/web/` if your game folder is `mygame/`). > When testing your modifications, it's a good idea to add `DEBUG = True` to > your settings file. This will give you nice informative tracebacks directly @@ -52,8 +39,7 @@ You'll mostly be doing so in your settings file > DEBUG mode leaks memory (for retaining debug info) and is *not* safe to use > for a production game! -As explained on the [Webserver](./Webserver.md) page, the process for getting a web -page is +As explained on the [Webserver](./Webserver.md) page, the process for getting a web page is 1. Web browser sends HTTP request to server with an URL 2. `urls.py` uses regex to match that URL to a _view_ (a Python function or callable class). @@ -65,8 +51,7 @@ page is the HTML page requires static resources are requested, the browser will fetch those separately before displaying it to the user. -If you look at the [evennia/web/](github:develop/evennia/web) directory you'll find the following -structure (leaving out stuff not relevant to the website): +If you look at the [evennia/web/](github:develop/evennia/web) directory you'll find the following structure (leaving out stuff not relevant to the website): ``` evennia/web/ @@ -91,8 +76,7 @@ structure (leaving out stuff not relevant to the website): ``` -The top-level `web/urls.py` file 'includes' the `web/website/urls.py` file - -that way all the website-related url-handling is kept in the same place. +The top-level `web/urls.py` file 'includes' the `web/website/urls.py` file - that way all the website-related url-handling is kept in the same place. This is the layout of the `mygame/web/` folder relevant for the website: @@ -124,18 +108,11 @@ This is the layout of the `mygame/web/` folder relevant for the website: ``` -As you can see, the `mygame/web/` folder is a copy of the `evennia/web/` folder -structure except the `mygame` folders are mostly empty. +As you can see, the `mygame/web/` folder is a copy of the `evennia/web/` folder structure except the `mygame` folders are mostly empty. -For static- and template-files, Evennia will _first_ -look in `mygame/static` and `mygame/templates` before going to the default -locations in `evennia/web/`. So override these resources, you just need to put -a file with the same name in the right spot under `mygame/web/` (and then -reload the server). Easiest is often to copy the original over and modify it. +For static- and template-files, Evennia will _first_ look in `mygame/static` and `mygame/templates` before going to the default locations in `evennia/web/`. So override these resources, you just need to put a file with the same name in the right spot under `mygame/web/` (and then reload the server). Easiest is often to copy the original over and modify it. -Overridden views (Python modules) also need an additional tweak to the -`website/urls.py` file - you must make sure to repoint the url to the new -version rather than it using the original. +Overridden views (Python modules) also need an additional tweak to the `website/urls.py` file - you must make sure to repoint the url to the new version rather than it using the original. ## Examples of commom web changes @@ -178,53 +155,26 @@ documents - they contain a special templating language marked with `{% ... %}` a Some important things to know: -- `{% extends "base.html" %}` - This is equivalent to a Python - `from othermodule import *` statement, but for templates. It allows a given template - to use everything from the imported (extended) template, but also to override anything - it wants to change. This makes it easy to keep all pages looking the same and avoids - a lot of boiler plate. -- `{% block blockname %}...{% endblock %}` - Blocks are inheritable, named pieces of code - that are modified in one place and then used elsewhere. This works a bit in reverse to - normal inheritance, because it's commonly in such a way that `base.html` defines an empty - block, let's say `contents`: `{% block contents %}{% endblock %}` but makes sure to put - that _in the right place_, say in the main body, next to the sidebar etc. Then each page - does `{% extends "base.html %"}` and makes their own `{% block contents} {% endblock %}`. - Their `contents` block will now override the empty one in `base.html` and appear in the right - place in the document, without the extending template having to specifying everything else +- `{% extends "base.html" %}` - This is equivalent to a Python `from othermodule import *` statement, but for templates. It allows a given template to use everything from the imported (extended) template, but also to override anything it wants to change. This makes it easy to keep all pages looking the same and avoids a lot of boiler plate. +- `{% block blockname %}...{% endblock %}` - Blocks are inheritable, named pieces of code that are modified in one place and then used elsewhere. This works a bit in reverse to normal inheritance, because it's commonly in such a way that `base.html` defines an empty block, let's say `contents`: `{% block contents %}{% endblock %}` but makes sure to put that _in the right place_, say in the main body, next to the sidebar etc. Then each page does `{% extends "base.html %"}` and makes their own `{% block contents} {% endblock %}`. Their `contents` block will now override the empty one in `base.html` and appear in the right place in the document, without the extending template having to specifying everything else around it! -- `{{ ... }}` are 'slots' usually embedded inside HTML tags or content. They reference a - _context_ (basically a dict) that the Python _view_ makes available to it. - Keys on the context are accessed with dot-notation, so if you provide a - context `{"stats": {"hp": 10, "mp": 5}}` to your template, you could access - that as `{{ stats.hp }}` to display `10` at that location to display `10` at - that location. +- `{{ ... }}` are 'slots' usually embedded inside HTML tags or content. They reference a _context_ (basically a dict) that the Python _view_ makes available to it. Keys on the context are accessed with dot-notation, so if you provide a context `{"stats": {"hp": 10, "mp": 5}}` to your template, you could access that as `{{ stats.hp }}` to display `10` at that location to display `10` at that location. -This allows for template inheritance (making it easier to make all -pages look the same without rewriting the same thing over and over) +This allows for template inheritance (making it easier to make all pages look the same without rewriting the same thing over and over) There's a lot more information to be found in the [Django template language documentation](https://docs.djangoproject.com/en/3.2/ref/templates/language/). ### Change webpage colors and styling -You can tweak the [CSS](https://en.wikipedia.org/wiki/Cascading_Style_Sheets) of the entire -website. If you investigate the `evennia/web/templates/website/base.html` file you'll see that we -use the [Bootstrap -4](https://getbootstrap.com/docs/4.6/getting-started/introduction/) toolkit. +You can tweak the [CSS](https://en.wikipedia.org/wiki/Cascading_Style_Sheets) of the entire website. If you investigate the `evennia/web/templates/website/base.html` file you'll see that we use the [Bootstrap 4](https://getbootstrap.com/docs/4.6/getting-started/introduction/) toolkit. -Much structural HTML functionality is actually coming from bootstrap, so you -will often be able to just add bootstrap CSS classes to elements in the HTML -file to get various effects like text-centering or similar. +Much structural HTML functionality is actually coming from bootstrap, so you will often be able to just add bootstrap CSS classes to elements in the HTML file to get various effects like text-centering or similar. -The website's custom CSS is found in -`evennia/web/static/website/css/website.css` but we also look for a (currently -empty) `custom.css` in the same location. You can override either, but it may -be easier to revert your changes if you only add things to `custom.css`. +The website's custom CSS is found in `evennia/web/static/website/css/website.css` but we also look for a (currently empty) `custom.css` in the same location. You can override either, but it may be easier to revert your changes if you only add things to `custom.css`. -Copy the CSS file you want to modify to the corresponding location in `mygame/web`. -Modify it and reload the server to see your changes. +Copy the CSS file you want to modify to the corresponding location in `mygame/web`. Modify it and reload the server to see your changes. -You can also apply static files without reloading, but running this in the -terminal: +You can also apply static files without reloading, but running this in the terminal: evennia collectstatic --no-input @@ -233,8 +183,7 @@ terminal: > Note that before you see new CSS files applied you may need to refresh your > browser without cache (Ctrl-F5 in Firefox, for example). -As an example, add/copy `custom.css` to `mygame/web/static/website/css/` and -add the following: +As an example, add/copy `custom.css` to `mygame/web/static/website/css/` and add the following: ```css @@ -258,8 +207,7 @@ Reload and your website now has a red theme! ### Change front page functionality -The logic is all in the view. To find where the index-page view is found, we -look in `evennia/web/website/urls.py`. Here we find the following line: +The logic is all in the view. To find where the index-page view is found, we look in `evennia/web/website/urls.py`. Here we find the following line: ```python # in evennia/web/website/urls.py @@ -275,8 +223,7 @@ The first `""` is the empty url - root - what you get if you just enter `localho with no extra path. As expected, this leads to the index page. By looking at the imports we find the view is in in `evennia/web/website/views/index.py`. -Copy this file to the corresponding location in `mygame/web`. Then tweak your `mygame/web/website/urls.py` -file to point to the new file: +Copy this file to the corresponding location in `mygame/web`. Then tweak your `mygame/web/website/urls.py` file to point to the new file: ```python # in mygame/web/website/urls.py @@ -293,45 +240,27 @@ urlpatterns = [ ``` -So we just import `index` from the new location and point to it. After a reload -the front page will now redirect to use your copy rather than the original. +So we just import `index` from the new location and point to it. After a reload the front page will now redirect to use your copy rather than the original. -The frontpage view is a class `EvenniaIndexView`. This is a [Django class-based view](https://docs.djangoproject.com/en/3.2/topics/class-based-views/). -It's a little less visible what happens in a class-based view than in a function (since -the class implements a lot of functionality as methods), but it's powerful and -much easier to extend/modify. +The frontpage view is a class `EvenniaIndexView`. This is a [Django class-based view](https://docs.djangoproject.com/en/3.2/topics/class-based-views/). It's a little less visible what happens in a class-based view than in a function (since the class implements a lot of functionality as methods), but it's powerful and much easier to extend/modify. -The class property `template_name` sets the location of the template used under -the `templates/` folder. So `website/index.html` points to -`web/templates/website/index.html` (as we already explored above. +The class property `template_name` sets the location of the template used under the `templates/` folder. So `website/index.html` points to `web/templates/website/index.html` (as we already explored above. -The `get_context_data` is a convenient method for providing the context for the -template. In the index-page's case we want the game stats (number of recent -players etc). These are then made available to use in `{{ ... }}` slots in the -template as described in the previous section. +The `get_context_data` is a convenient method for providing the context for the template. In the index-page's case we want the game stats (number of recent players etc). These are then made available to use in `{{ ... }}` slots in the template as described in the previous section. ### Change other website pages -The other sub pages are handled in the same way - copy the template or static -resource to the right place, or copy the view and repoint your `website/urls.py` to -your copy. Just remember to reload. +The other sub pages are handled in the same way - copy the template or static resource to the right place, or copy the view and repoint your `website/urls.py` to your copy. Just remember to reload. ## Adding a new web page ### Using Flat Pages -The absolutely simplest way to add a new web page is to use the `Flat Pages` -app available in the [Web Admin](./Web-Admin.md). The page will appear with the same -styling as the rest of the site. +The absolutely simplest way to add a new web page is to use the `Flat Pages` app available in the [Web Admin](./Web-Admin.md). The page will appear with the same styling as the rest of the site. -For the `Flat pages` module to work you must first set up a _Site_ (or -domain) to use. You only need to this once. +For the `Flat pages` module to work you must first set up a _Site_ (or domain) to use. You only need to this once. -- Go to the Web admin and select `Sites`. If your -game is at `mygreatgame.com`, that's the domain you need to add. For local -experimentation, add the domain `localhost:4001`. Note the `id` of the domain -(look at the url when you click on the new domain, if it's for example -`http://localhost:4001/admin/sites/site/2/change/`, then the id is `2`). +- Go to the Web admin and select `Sites`. If your game is at `mygreatgame.com`, that's the domain you need to add. For local experimentation, add the domain `localhost:4001`. Note the `id` of the domain (look at the url when you click on the new domain, if it's for example `http://localhost:4001/admin/sites/site/2/change/`, then the id is `2`). - Now add the line `SITE_ID = ` to your settings file. Next you create new pages easily. @@ -348,17 +277,13 @@ You can now go to `localhost:4001/test/` and see your new page! ### Add Custom new page -The `Flat Pages` page doesn't allow for (much) dynamic content and customization. For -this you need to add the needed components yourself. +The `Flat Pages` page doesn't allow for (much) dynamic content and customization. For this you need to add the needed components yourself. Let's see how to make a `/test/` page from scratch. -- Add a new `test.html` file under `mygame/web/templates/website/`. Easiest is to base - this off an existing file. Make sure to `{% extend base.html %}` if you want to - get the same styling as the rest of your site. +- Add a new `test.html` file under `mygame/web/templates/website/`. Easiest is to base this off an existing file. Make sure to `{% extend base.html %}` if you want to get the same styling as the rest of your site. - Add a new view `testview.py` under `mygame/web/website/views/` (don't name it `test.py` or - Django/Evennia will think it contains unit tests). Add a view there to process - your page. This is a minimal view to start from (read much more [in the Django docs](https://docs.djangoproject.com/en/3.2/topics/class-based-views/)): + Django/Evennia will think it contains unit tests). Add a view there to process your page. This is a minimal view to start from (read much more [in the Django docs](https://docs.djangoproject.com/en/3.2/topics/class-based-views/)): ```python # mygame/web/website/views/testview.py @@ -416,5 +341,4 @@ The form is then linked into the view-class by adding `form_class = MyFormClass` the view (next to `template_name`). There are several example forms in `evennia/web/website/forms.py`. It's also a good -idea to read [Building a form in Django](https://docs.djangoproject.com/en/3.2/topics/forms/#building-a-form-in-django) -on the Django website - it covers all you need. +idea to read [Building a form in Django](https://docs.djangoproject.com/en/3.2/topics/forms/#building-a-form-in-django) on the Django website - it covers all you need. diff --git a/docs/source/Concepts/Async-Process.md b/docs/source/Concepts/Async-Process.md index fa11ca3515..c444fad562 100644 --- a/docs/source/Concepts/Async-Process.md +++ b/docs/source/Concepts/Async-Process.md @@ -1,14 +1,17 @@ # Async Process -*This is considered an advanced topic.* +```{important} +This is considered an advanced topic. +``` ## Synchronous versus Asynchronous -Most program code operates *synchronously*. This means that each statement in your code gets -processed and finishes before the next can begin. This makes for easy-to-understand code. It is also -a *requirement* in many cases - a subsequent piece of code often depend on something calculated or -defined in a previous statement. +```{sidebar} +This is also explored in the [Command Duration Tutorial](../Howtos/Howto-Command-Duration.md). +``` + +Most program code operates *synchronously*. This means that each statement in your code gets processed and finishes before the next can begin. This makes for easy-to-understand code. It is also a *requirement* in many cases - a subsequent piece of code often depend on something calculated or defined in a previous statement. Consider this piece of code in a traditional Python program: @@ -16,27 +19,94 @@ Consider this piece of code in a traditional Python program: print("before call ...") long_running_function() print("after call ...") - ``` When run, this will print `"before call ..."`, after which the `long_running_function` gets to work -for however long time. Only once that is done, the system prints `"after call ..."`. Easy and -logical to follow. Most of Evennia work in this way and often it's important that commands get +for however long time. Only once that is done, the system prints `"after call ..."`. Easy and logical to follow. Most of Evennia work in this way and often it's important that commands get executed in the same strict order they were coded. -Evennia, via Twisted, is a single-process multi-user server. In simple terms this means that it -swiftly switches between dealing with player input so quickly that each player feels like they do -things at the same time. This is a clever illusion however: If one user, say, runs a command -containing that `long_running_function`, *all* other players are effectively forced to wait until it -finishes. +Evennia, via Twisted, is a single-process multi-user server. In simple terms this means that it swiftly switches between dealing with player input so quickly that each player feels like they do things at the same time. This is a clever illusion however: If one user, say, runs a command containing that `long_running_function`, *all* other players are effectively forced to wait until it finishes. -Now, it should be said that on a modern computer system this is rarely an issue. Very few commands -run so long that other users notice it. And as mentioned, most of the time you *want* to enforce -all commands to occur in strict sequence. +Now, it should be said that on a modern computer system this is rarely an issue. Very few commands run so long that other users notice it. And as mentioned, most of the time you *want* to enforce all commands to occur in strict sequence. -When delays do become noticeable and you don't care in which order the command actually completes, -you can run it *asynchronously*. This makes use of the `run_async()` function in -`src/utils/utils.py`: +## `utils.delay` + +```{sidebar} delay() vs time.sleep() +This is equivalent to something like `time.sleep()` except `delay` is asynchronous while `sleep` would lock the entire server for the duration of the sleep. +``` +The `delay` function is a much simpler sibling to `run_async`. It is in fact just a way to delay the execution of a command until a future time. + +```python + from evennia.utils import delay + + # [...] + # e.g. inside a Command, where `self.caller` is available + def callback(obj): + obj.msg("Returning!") + delay(10, callback, self.caller) +``` + +This will delay the execution of the callback for 10 seconds. Provide `persistent=True` to make the delay survive a server `reload`. While waiting, you can input commands normally. + +You can also try the following snippet just see how it works: + + py from evennia.utils import delay; delay(10, lambda who: who.msg("Test!"), self) + +Wait 10 seconds and 'Test!' should be echoed back to you. + + +## `@utils.interactive` decorator + +The `@interactive` [decorator](https://realpython.com/primer-on-python- decorators/) makes any function or method possible to 'pause' and/or await player input in an interactive way. + +```python + from evennia.utils import interactive + + @interactive + def myfunc(caller): + + while True: + caller.msg("Getting ready to wait ...") + yield(5) + caller.msg("Now 5 seconds have passed.") + + response = yield("Do you want to wait another 5 secs?") + + if response.lower() not in ("yes", "y"): + break +``` + +The `@interactive` decorator gives the function the ability to pause. The use of `yield(seconds)` will do just that - it will asynchronously pause for the number of seconds given before continuing. This is technically equivalent to using `call_async` with a callback that continues after 5 secs. But the code with `@interactive` is a little easier to follow. + +Within the `@interactive` function, the `response = yield("question")` question allows you to ask the user for input. You can then process the input, just like you would if you used the Python `input` function. + +All of this makes the `@interactive` decorator very useful. But it comes with a few caveats. + +- The decorated function/method/callable must have an argument named exactly `caller`. Evennia will look for an argument with this name and treat it as the source of input. +- Decorating a function this way turns it turns it into a Python [generator](https://wiki.python.org/moin/Generators). This means + - You can't use `return ` from a generator (just an empty `return` works). To return a value from a function/method you have decorated with `@interactive`, you must instead use a special Twisted function `twisted.internet.defer.returnValue`. Evennia also makes this function conveniently available from `evennia.utils`: + + ```python + from evennia.utils import interactive, returnValue + + @interactive + def myfunc(): + + # ... + result = 10 + + # this must be used instead of `return result` + returnValue(result) + ``` + + +## `utils.run_async` + +```{warning} +Unless you have a very clear purpose in mind, you are unlikely to get an expected result from `run_async`. Notably, it will still run your long-running function _in the same thread_ as the rest of the server. So while it does run async, a very heavy and CPU-heavy operation will still block the server. So don't consider this as a way to offload heavy operations without affecting the rest of the server. +``` + +When you don't care in which order the command actually completes, you can run it *asynchronously*. This makes use of the `run_async()` function in `src/utils/utils.py`: ```python run_async(function, *args, **kwargs) @@ -51,12 +121,7 @@ Where `function` will be called asynchronously with `*args` and `**kwargs`. Exam print("after call ...") ``` -Now, when running this you will find that the program will not wait around for -`long_running_function` to finish. In fact you will see `"before call ..."` and `"after call ..."` -printed out right away. The long-running function will run in the background and you (and other -users) can go on as normal. - -## Customizing asynchronous operation +Now, when running this you will find that the program will not wait around for `long_running_function` to finish. In fact you will see `"before call ..."` and `"after call ..."` printed out right away. The long-running function will run in the background and you (and other users) can go on as normal. A complication with using asynchronous calls is what to do with the result from that call. What if `long_running_function` returns a value that you need? It makes no real sense to put any lines of @@ -75,8 +140,7 @@ line quite pointless for processing any data from the function. Instead one has print(r) ``` -- `at_return_kwargs` - an optional dictionary that will be fed as keyword arguments to the -`at_return` callback. +- `at_return_kwargs` - an optional dictionary that will be fed as keyword arguments to the `at_return` callback. - `at_err(e)` (the *errback*) is called if the asynchronous function fails and raises an exception. This exception is passed to the errback wrapped in a *Failure* object `e`. If you do not supply an errback of your own, Evennia will automatically add one that silently writes errors to the evennia @@ -116,119 +180,6 @@ An example of making an asynchronous call from inside a [Command](../Components/ at_err=at_err_function) ``` -That's it - from here on we can forget about `long_running_function` and go on with what else need -to be done. *Whenever* it finishes, the `at_return_function` function will be called and the final -value will -pop up for us to see. If not we will see an error message. +That's it - from here on we can forget about `long_running_function` and go on with what else need to be done. *Whenever* it finishes, the `at_return_function` function will be called and the final value will pop up for us to see. If not we will see an error message. -## delay - -The `delay` function is a much simpler sibling to `run_async`. It is in fact just a way to delay the -execution of a command until a future time. This is equivalent to something like `time.sleep()` -except delay is asynchronous while `sleep` would lock the entire server for the duration of the -sleep. - -```python - from evennia.utils import delay - - # [...] - # e.g. inside a Command, where `self.caller` is available - def callback(obj): - obj.msg("Returning!") - delay(10, callback, self.caller) -``` - -This will delay the execution of the callback for 10 seconds. This function is explored much more in -the [Command Duration Tutorial](../Howtos/Howto-Command-Duration.md). - -You can also try the following snippet just see how it works: - - @py from evennia.utils import delay; delay(10, lambda who: who.msg("Test!"), self) - -Wait 10 seconds and 'Test!' should be echoed back to you. - - -## The @interactive decorator - -As of Evennia 0.9, the `@interactive` [decorator](https://realpython.com/primer-on-python- -decorators/) -is available. This makes any function or method possible to 'pause' and/or await player input -in an interactive way. - -```python - from evennia.utils import interactive - - @interactive - def myfunc(caller): - - while True: - caller.msg("Getting ready to wait ...") - yield(5) - caller.msg("Now 5 seconds have passed.") - - response = yield("Do you want to wait another 5 secs?") - - if response.lower() not in ("yes", "y"): - break -``` - -The `@interactive` decorator gives the function the ability to pause. The use -of `yield(seconds)` will do just that - it will asynchronously pause for the -number of seconds given before continuing. This is technically equivalent to -using `call_async` with a callback that continues after 5 secs. But the code -with `@interactive` is a little easier to follow. - -Within the `@interactive` function, the `response = yield("question")` question -allows you to ask the user for input. You can then process the input, just like -you would if you used the Python `input` function. There is one caveat to this -functionality though - _it will only work if the function/method has an -argument named exactly `caller`_. This is because internally Evennia will look -for the `caller` argument and treat that as the source of input. - -All of this makes the `@interactive` decorator very useful. But it comes with a -few caveats. Notably, decorating a function/method with `@interactive` turns it -into a Python [generator](https://wiki.python.org/moin/Generators). The most -common issue is that you cannot use `return ` from a generator (just an -empty `return` works). To return a value from a function/method you have decorated -with `@interactive`, you must instead use a special Twisted function -`twisted.internet.defer.returnValue`. Evennia also makes this function -conveniently available from `evennia.utils`: - -```python - from evennia.utils import interactive, returnValue - - @interactive - def myfunc(): - - # ... - result = 10 - - # this must be used instead of `return result` - returnValue(result) - -``` - - - -## Assorted notes - -Overall, be careful with choosing when to use asynchronous calls. It is mainly useful for large -administration operations that have no direct influence on the game world (imports and backup -operations come to mind). Since there is no telling exactly when an asynchronous call actually ends, -using them for in-game commands is to potentially invite confusion and inconsistencies (and very -hard-to-reproduce bugs). - -The very first synchronous example above is not *really* correct in the case of Twisted, which is -inherently an asynchronous server. Notably you might find that you will *not* see the first `before -call ...` text being printed out right away. Instead all texts could end up being delayed until -after the long-running process finishes. So all commands will retain their relative order as -expected, but they may appear with delays or in groups. - -## Further reading - -Technically, `run_async` is just a very thin and simplified wrapper around a -[Twisted Deferred](https://twistedmatrix.com/documents/9.0.0/core/howto/defer.html) object; the -wrapper sets -up a default errback also if none is supplied. If you know what you are doing there is nothing -stopping you from bypassing the utility function, building a more sophisticated callback chain after -your own liking. \ No newline at end of file +> Technically, `run_async` is just a very thin and simplified wrapper around a [Twisted Deferred](https://twistedmatrix.com/documents/9.0.0/core/howto/defer.html) object; the wrapper sets up a default errback also if none is supplied. If you know what you are doing there is nothing stopping you from bypassing the utility function, building a more sophisticated callback chain after your own liking. \ No newline at end of file diff --git a/docs/source/Concepts/Banning.md b/docs/source/Concepts/Banning.md index 2c8b5c22c8..46c4e33b40 100644 --- a/docs/source/Concepts/Banning.md +++ b/docs/source/Concepts/Banning.md @@ -7,10 +7,7 @@ admin tools to handle this, primarily `ban`, `unban`, and `boot`. ## Creating a ban -Say we have a troublesome player "YouSuck" - this is a person that refuses common courtesy - an -abusive -and spammy account that is clearly created by some bored internet hooligan only to cause grief. You -have tried to be nice. Now you just want this troll gone. +Say we have a troublesome player "YouSuck" - this is a person that refuses common courtesy - an abusive and spammy account that is clearly created by some bored internet hooligan only to cause grief. You have tried to be nice. Now you just want this troll gone. ### Name ban @@ -18,16 +15,13 @@ The easiest recourse is to block the account YouSuck from ever connecting again. ban YouSuck -This will lock the name YouSuck (as well as 'yousuck' and any other capitalization combination), and -next time they try to log in with this name the server will not let them! +This will lock the name YouSuck (as well as 'yousuck' and any other capitalization combination), and next time they try to log in with this name the server will not let them! -You can also give a reason so you remember later why this was a good thing (the banned account will -never see this) +You can also give a reason so you remember later why this was a good thing (the banned account will never see this) ban YouSuck:This is just a troll. -If you are sure this is just a spam account, you might even consider deleting the player account -outright: +If you are sure this is just a spam account, you might even consider deleting the player account outright: accounts/delete YouSuck @@ -36,9 +30,7 @@ change your mind you can always remove the block later whereas a deletion is per ### IP ban -Just because you block YouSuck's name might not mean the trolling human behind that account gives -up. They can just create a new account YouSuckMore and be back at it. One way to make things harder -for them is to tell the server to not allow connections from their particular IP address. +Just because you block YouSuck's name might not mean the trolling human behind that account gives up. They can just create a new account YouSuckMore and be back at it. One way to make things harder for them is to tell the server to not allow connections from their particular IP address. First, when the offending account is online, check which IP address they use. This you can do with the `who` command, which will show you something like this: @@ -46,42 +38,31 @@ the `who` command, which will show you something like this: Account Name On for Idle Room Cmds Host YouSuckMore 01:12 2m 22 212 237.333.0.223 -The "Host" bit is the IP address from which the account is connecting. Use this to define the ban -instead of the name: +The "Host" bit is the IP address from which the account is connecting. Use this to define the ban instead of the name: ban 237.333.0.223 -This will stop YouSuckMore connecting from their computer. Note however that IP address might change -easily - either due to how the player's Internet Service Provider operates or by the user simply -changing computers. You can make a more general ban by putting asterisks `*` as wildcards for the -groups of three digits in the address. So if you figure out that !YouSuckMore mainly connects from -237.333.0.223, 237.333.0.225, and 237.333.0.256 (only changes in their subnet), it might be an idea -to put down a ban like this to include any number in that subnet: +This will stop YouSuckMore connecting from their computer. Note however that IP address might change easily - either due to how the player's Internet Service Provider operates or by the user simply changing computers. You can make a more general ban by putting asterisks `*` as wildcards for the groups of three digits in the address. So if you figure out that !YouSuckMore mainly connects from `237.333.0.223`, `237.333.0.225`, and `237.333.0.256` (only changes in their subnet), it might be an idea to put down a ban like this to include any number in that subnet: ban 237.333.0.* -You should combine the IP ban with a name-ban too of course, so the account YouSuckMore is truly -locked regardless of where they connect from. +You should combine the IP ban with a name-ban too of course, so the account YouSuckMore is truly locked regardless of where they connect from. -Be careful with too general IP bans however (more asterisks above). If you are unlucky you could be -blocking out innocent players who just happen to connect from the same subnet as the offender. +Be careful with too general IP bans however (more asterisks above). If you are unlucky you could be blocking out innocent players who just happen to connect from the same subnet as the offender. ## Booting -YouSuck is not really noticing all this banning yet though - and won't until having logged out and -trying to log back in again. Let's help the troll along. +YouSuck is not really noticing all this banning yet though - and won't until having logged out and trying to log back in again. Let's help the troll along. boot YouSuck -Good riddance. You can give a reason for booting too (to be echoed to the player before getting -kicked out). +Good riddance. You can give a reason for booting too (to be echoed to the player before getting kicked out). boot YouSuck:Go troll somewhere else. ### Lifting a ban -Use the `unban` (or `ban`) command without any arguments and you will see a list of all currently -active bans: +Use the `unban` (or `ban`) command without any arguments and you will see a list of all currently active bans: Active bans id name/ip date reason @@ -113,32 +94,21 @@ is not what you want in this case. - **unban 34** -- Remove ban with id #34 - **cboot mychannel = thomas** -- Boot a subscriber from a channel you control -- **clock mychannel = control:perm(Admin);listen:all();send:all()** -- Fine control of access to -your channel using [lock definitions](../Components/Locks.md). +- **clock mychannel = control:perm(Admin);listen:all();send:all()** -- Fine control of access to your channel using [lock definitions](../Components/Locks.md). Locking a specific command (like `page`) is accomplished like so: -1. Examine the source of the command. [The default `page` command class]( -https://github.com/evennia/evennia/blob/master/evennia/commands/default/comms.py#L686) has the lock -string **"cmd:not pperm(page_banned)"**. This means that unless the player has the 'permission' -"page_banned" they can use this command. You can assign any lock string to allow finer customization -in your commands. You might look for the value of an [Attribute](../Components/Attributes.md) or [Tag](../Components/Tags.md), your -current location etc. -2. **perm/account thomas = page_banned** -- Give the account the 'permission' which causes (in this -case) the lock to fail. - -- **perm/del/account thomas = page_banned** -- Remove the given permission +1. Examine the source of the command. [The default `page` command class]( https://github.com/evennia/evennia/blob/master/evennia/commands/default/comms.py#L686) has the lock string **"cmd:not pperm(page_banned)"**. This means that unless the player has the 'permission' "page_banned" they can use this command. You can assign any lock string to allow finer customization in your commands. You might look for the value of an [Attribute](../Components/Attributes.md) or [Tag](../Components/Tags.md), your current location etc. +2. **perm/account thomas = page_banned** -- Give the account the 'permission' which causes (in this case) the lock to fail. +- **perm/del/account thomas = page_banned** -- Remove the given permission - **tel thomas = jail** -- Teleport a player to a specified location or #dbref -- **type thomas = FlowerPot** -- Turn an annoying player into a flower pot (assuming you have a -`FlowerPot` typeclass ready) +- **type thomas = FlowerPot** -- Turn an annoying player into a flower pot (assuming you have a `FlowerPot` typeclass ready) - **userpassword thomas = fooBarFoo** -- Change a user's password - **accounts/delete thomas** -- Delete a player account (not recommended, use **ban** instead) -- **server** -- Show server statistics, such as CPU load, memory usage, and how many objects are -cached +- **server** -- Show server statistics, such as CPU load, memory usage, and how many objects are cached - **time** -- Gives server uptime, runtime, etc - **reload** -- Reloads the server without disconnecting anyone - **reset** -- Restarts the server, kicking all connections - **shutdown** -- Stops the server cold without it auto-starting again -- **py** -- Executes raw Python code, allows for direct inspection of the database and account -objects on the fly. For advanced users. +- **py** -- Executes raw Python code, allows for direct inspection of the database and account objects on the fly. For advanced users. diff --git a/docs/source/Concepts/Bootstrap-&-Evennia.md b/docs/source/Concepts/Bootstrap-&-Evennia.md deleted file mode 100644 index 8f9781af51..0000000000 --- a/docs/source/Concepts/Bootstrap-&-Evennia.md +++ /dev/null @@ -1,101 +0,0 @@ -# Bootstrap & Evennia - -# What is Bootstrap? -Evennia's new default web page uses a framework called [Bootstrap](https://getbootstrap.com/). This -framework is in use across the internet - you'll probably start to recognize its influence once you -learn some of the common design patterns. This switch is great for web developers, perhaps like -yourself, because instead of wondering about setting up different grid systems or what custom class -another designer used, we have a base, a bootstrap, to work from. Bootstrap is responsive by -default, and comes with some default styles that Evennia has lightly overrode to keep some of the -same colors and styles you're used to from the previous design. - -For your reading pleasure, a brief overview of Bootstrap follows. For more in-depth info, please -read [the documentation](https://getbootstrap.com/docs/4.0/getting-started/introduction/). -*** - -## The Layout System -Other than the basic styling Bootstrap includes, it also includes [a built in layout and grid -system](https://getbootstrap.com/docs/4.0/layout/overview/). -The first part of this system is [the -container](https://getbootstrap.com/docs/4.0/layout/overview/#containers). - -The container is meant to hold all your page content. Bootstrap provides two types: fixed-width and -full-width. -Fixed-width containers take up a certain max-width of the page - they're useful for limiting the -width on Desktop or Tablet platforms, instead of making the content span the width of the page. -``` -
- -
-``` -Full width containers take up the maximum width available to them - they'll span across a wide- -screen desktop or a smaller screen phone, edge-to-edge. -``` -
- -
-``` - -The second part of the layout system is [the grid](https://getbootstrap.com/docs/4.0/layout/grid/). -This is the bread-and-butter of the layout of Bootstrap - it allows you to change the size of -elements depending on the size of the screen, without writing any media queries. We'll briefly go -over it - to learn more, please read the docs or look at the source code for Evennia's home page in -your browser. -> Important! Grid elements should be in a .container or .container-fluid. This will center the -contents of your site. - -Bootstrap's grid system allows you to create rows and columns by applying classes based on -breakpoints. The default breakpoints are extra small, small, medium, large, and extra-large. If -you'd like to know more about these breakpoints, please [take a look at the documentation for -them.](https://getbootstrap.com/docs/4.0/layout/overview/#responsive-breakpoints) - -To use the grid system, first create a container for your content, then add your rows and columns -like so: -``` -
-
-
- 1 of 3 -
-
- 2 of 3 -
-
- 3 of 3 -
-
-
-``` -This layout would create three equal-width columns. - -To specify your sizes - for instance, Evennia's default site has three columns on desktop and -tablet, but reflows to single-column on smaller screens. Try it out! -``` -
-
-
- 1 of 4 -
-
- 2 of 4 -
-
- 3 of 4 -
-
- 4 of 4 -
-
-
-``` -This layout would be 4 columns on large screens, 2 columns on medium screens, and 1 column on -anything smaller. - -To learn more about Bootstrap's grid, please [take a look at the -docs](https://getbootstrap.com/docs/4.0/layout/grid/) -*** - -## More Bootstrap -Bootstrap also provides a huge amount of utilities, as well as styling and content elements. To -learn more about them, please [read the Bootstrap docs](https://getbootstrap.com/docs/4.0/getting- -started/introduction/) or read one of our other web tutorials. \ No newline at end of file diff --git a/docs/source/Concepts/Building-Permissions.md b/docs/source/Concepts/Building-Permissions.md deleted file mode 100644 index 728a64f41b..0000000000 --- a/docs/source/Concepts/Building-Permissions.md +++ /dev/null @@ -1,72 +0,0 @@ -# Building Permissions - - -*OBS: This gives only a brief introduction to the access system. Locks and permissions are fully -detailed* [here](../Components/Locks.md). - -## The super user - -There are strictly speaking two types of users in Evennia, the *super user* and everyone else. The -superuser is the first user you create, object `#1`. This is the all-powerful server-owner account. -Technically the superuser not only has access to everything, it *bypasses* the permission checks -entirely. This makes the superuser impossible to lock out, but makes it unsuitable to actually play- -test the game's locks and restrictions with (see `@quell` below). Usually there is no need to have -but one superuser. - -## Assigning permissions - -Whereas permissions can be used for anything, those put in `settings.PERMISSION_HIERARCHY` will have -a ranking relative each other as well. We refer to these types of permissions as *hierarchical -permissions*. When building locks to check these permissions, the `perm()` [lock function](../Components/Locks.md) is -used. By default Evennia creates the following hierarchy (spelled exactly like this): - -1. **Developers** basically have the same access as superusers except that they do *not* sidestep -the Permission system. Assign only to really trusted server-admin staff since this level gives -access both to server reload/shutdown functionality as well as (and this may be more critical) gives -access to the all-powerful `@py` command that allows the execution of arbitrary Python code on the -command line. -1. **Admins** can do everything *except* affecting the server functions themselves. So an Admin -couldn't reload or shutdown the server for example. They also cannot execute arbitrary Python code -on the console or import files from the hard drive. -1. **Builders** - have all the build commands, but cannot affect other accounts or mess with the -server. -1. **Helpers** are almost like a normal *Player*, but they can also add help files to the database. -1. **Players** is the default group that new players end up in. A new player have permission to use -tells and to use and create new channels. - -A user having a certain level of permission automatically have access to locks specifying access of -a lower level. - -To assign a new permission from inside the game, you need to be able to use the `@perm` command. -This is an *Developer*-level command, but it could in principle be made lower-access since it only -allows assignments equal or lower to your current level (so you cannot use it to escalate your own -permission level). So, assuming you yourself have *Developer* access (or is superuser), you assign -a new account "Tommy" to your core staff with the command - - @perm/account Tommy = Developer - -or - - @perm *Tommy = Developer - -We use a switch or the `*name` format to make sure to put the permission on the *Account* and not on -any eventual *Character* that may also be named "Tommy". This is usually what you want since the -Account will then remain an Developer regardless of which Character they are currently controlling. -To limit permission to a per-Character level you should instead use *quelling* (see below). Normally -permissions can be any string, but for these special hierarchical permissions you can also use -plural ("Developer" and "Developers" both grant the same powers). - -## Quelling your permissions - -When developing it can be useful to check just how things would look had your permission-level been -lower. For this you can use *quelling*. Normally, when you puppet a Character you are using your -Account-level permission. So even if your Character only has *Accounts* level permissions, your -*Developer*-level Account will take precedence. With the `@quell` command you can change so that the -Character's permission takes precedence instead: - - @quell - -This will allow you to test out the game using the current Character's permission level. A developer -or builder can thus in principle maintain several test characters, all using different permission -levels. Note that you cannot escalate your permissions this way; If the Character happens to have a -*higher* permission level than the Account, the *Account's* (lower) permission will still be used. \ No newline at end of file diff --git a/docs/source/Concepts/Change-Messages-Per-Receiver.md b/docs/source/Concepts/Change-Message-Per-Receiver.md similarity index 74% rename from docs/source/Concepts/Change-Messages-Per-Receiver.md rename to docs/source/Concepts/Change-Message-Per-Receiver.md index b1468f4ac2..1be567da6f 100644 --- a/docs/source/Concepts/Change-Messages-Per-Receiver.md +++ b/docs/source/Concepts/Change-Message-Per-Receiver.md @@ -1,7 +1,6 @@ -# Sending different messages depending on viewpoint and receiver +# Messages varying per receiver -Sending messages to everyong in a location is handled by the -[msg_contents](evennia.objects.objects.DefaultObject.msg_contents) method on +Sending messages to everyong in a location is handled by the [msg_contents](evennia.objects.objects.DefaultObject.msg_contents) method on all [Objects](../Components/Objects.md). It's most commonly called on rooms. ```python @@ -19,26 +18,21 @@ room.msg_contents("{anna} walks into the room.", Use `exclude=object_or_list_of_object` to skip sending the message one or more targets. -The advantage of this is that `anna_object.get_display_name(looker)` will be called -for every onlooker; this allows the `{anna}` stanza to be different depending on who -sees the strings. How this is to work depends on the _stance_ of your game. +The advantage of this is that `anna_object.get_display_name(looker)` will be called for every onlooker; this allows the `{anna}` stanza to be different depending on who sees the strings. How this is to work depends on the _stance_ of your game. The stance indicates how your game echoes its messages to the player. Knowing how you want to -handle the stance is important for a text game. There are two main stances that are usually considered, -_Actor stance_ and _Director stance_. +handle the stance is important for a text game. There are two main stances that are usually considered, _Actor stance_ and _Director stance_. | Stance | You see | Others in the same location see | | --- | --- | --- | | Actor stance | You pick up the stone | Anna picks up the stone | |Director stance | Anna picks up the stone | Anna picks up the stone | -It's not unheard of to mix the two stances - with commands from the game being told -in Actor stance while Director stance is used for complex emoting and roleplaying. One should -usually try to be consistent however. +It's not unheard of to mix the two stances - with commands from the game being told in Actor stance while Director stance is used for complex emoting and roleplaying. One should usually try to be consistent however. ## Director Stance -While not so common as Actor stance, director stance has the advantage of simplicity, particularly +While not as common as Actor stance, director stance has the advantage of simplicity, particularly in roleplaying MUDs where longer roleplaying emotes are used. It is also a pretty simple stance to implement technically since everyone sees the same text, regardless of viewpoint. @@ -60,8 +54,7 @@ technically, it's also easy to write for the player. ## Actor Stance -This means that the game addresses "you" when it does things. In actor stance, whenever you perform -an action, you should get a different message than those _observing_ you doing that action. +This means that the game addresses "you" when it does things. In actor stance, whenever you perform an action, you should get a different message than those _observing_ you doing that action. Tom picks up the gun, whistling to himself. @@ -69,16 +62,9 @@ This is what _others_ should see. The player themselves should see this: You pick up the gun, whistling to yourself. -Not only do you need to map "Tom" to "You" above, there are also grammatical differences - -"Tom walks" vs "You walk" and "himself" vs "yourself". This is a lot more complex to handle. For a -developer making simple "You/Tom pick/picks up the stone" messages, you could in principle hand-craft -the strings from every view point, but there's a better way. - -The `msg_contents` method helps by parsing the ingoing string with a -[FuncParser functions](../Components/FuncParser.md) with some very specific `$inline-functions`. The inline funcs -basically provides you with a mini-language for building _one_ string that will change -appropriately depending on who sees it. +Not only do you need to map "Tom" to "You" above, there are also grammatical differences - "Tom walks" vs "You walk" and "himself" vs "yourself". This is a lot more complex to handle. For a developer making simple "You/Tom pick/picks up the stone" messages, you could in principle hand-craft the strings from every view point, but there's a better way. +The `msg_contents` method helps by parsing the ingoing string with a [FuncParser functions](../Components/FuncParser.md) with some very specific `$inline-functions`. The inline funcs basically provides you with a mini-language for building _one_ string that will change appropriately depending on who sees it. ```python text = "$You() $conj(pick) up the gun, whistling to $pron(yourself)." @@ -93,8 +79,7 @@ These are the inline-functions available: to `picks`). Enter the root form of the verb. - `$pron(pronoun[,options])` - A pronoun is a word you want to use instead of a proper noun, like _him_, _herself_, _its_, _me_, _I_, _their_ and so on. The `options` is a space- or comma-separated - set of options to help the system map your pronoun from 1st/2nd person to 3rd person and vice versa. - See next section. + set of options to help the system map your pronoun from 1st/2nd person to 3rd person and vice versa. See next section. ### More on $pron() @@ -117,15 +102,7 @@ it translates between this table ... | **3rd person neutral** | it | it | its | theirs* | itself | | **3rd person plural** | they | them | their | theirs | themselves | -> *) The neutral 3rd person possessive pronoun is not actually used in English. We set it to "theirs" -> just to have something to show should someone accidentally ask for a neutral possessive pronoun. - -Some mappings are easy. For example, if you write `$pron(yourselves)` then the 3rd-person -form is always `themselves`. But because English grammar is the way it is, not all mappings -are 1:1. For example, if you write -`$pron(you)`, Evennia will not know which 3rd-persion equivalent this should map to - you need to -provide more info to help out. This can either be provided as a second space-separated option -to `$pron` or the system will try to figure it out on its own. +Some mappings are easy. For example, if you write `$pron(yourselves)` then the 3rd-person form is always `themselves`. But because English grammar is the way it is, not all mappings are 1:1. For example, if you write `$pron(you)`, Evennia will not know which 3rd-persion equivalent this should map to - you need to provide more info to help out. This can either be provided as a second space-separated option to `$pron` or the system will try to figure it out on its own. - `pronoun_type` - this is one of the columns in the table and can be set as a `$pron` option. @@ -176,14 +153,11 @@ to `$pron` or the system will try to figure it out on its own. | `$pron(her, 1)` | I | her | 3rd person -> 1st person | | `$pron(its, 1st)` | my | its | 3rd person -> 1st person | - -Note the three last examples - instead of specifying the 2nd person form you -can also specify the 3rd-person and do a 'reverse' lookup - you will still see the proper 1st/2nd text. -So writing `$pron(her)` instead of `$pron(you, op f)` gives the same result. +Note the three last examples - instead of specifying the 2nd person form you can also specify the 3rd-person and do a 'reverse' lookup - you will still see the proper 1st/2nd text. So writing `$pron(her)` instead of `$pron(you, op f)` gives the same result. The [$pron inlinefunc api is found here](evennia.utils.funcparser.funcparser_callable_pronoun) -# Referencing other objects +## Referencing other objects There is one more inlinefunc understood by `msg_contents`. This can be used natively to spruce up your strings (for both director- and actor stance): @@ -205,16 +179,11 @@ text = "$You() $conj(pick) up the $obj(gun), whistling to $pron(yourself)" room.msg_contents(text, from_obj=caller, mapping={"gun": gun_object}) ``` -Depending on your game, Tom may now see himself picking up `A rusty old gun`, whereas an onlooker -with a high gun smith skill may instead see him picking up `A rare-make Smith & Wesson model 686 -in poor condition" ...` +Depending on your game, Tom may now see himself picking up `A rusty old gun`, whereas an onlooker with a high gun smith skill may instead see him picking up `A rare-make Smith & Wesson model 686 in poor condition" ...` -# Recog systems and roleplaying +## Recog systems and roleplaying The `$funcparser` inline functions are very powerful for the game developer, but they may be a bit too much to write for the regular player. -The [rpsystem contrib](evennia.contrib.rpg.rpsystem) implements a full dynamic emote/pose and recognition -system with short-descriptions and disguises. It uses director stance with a custom markup -language, like `/me` `/gun` and `/tall man` to refer to players and objects in the location. It can be -worth checking out for inspiration. +The [rpsystem contrib](evennia.contrib.rpg.rpsystem) implements a full dynamic emote/pose and recognition system with short-descriptions and disguises. It uses director stance with a custom markup language, like `/me` `/gun` and `/tall man` to refer to players and objects in the location. It can be worth checking out for inspiration. diff --git a/docs/source/Concepts/Colors.md b/docs/source/Concepts/Colors.md index aa7a36d0e9..1b0a9b9c24 100644 --- a/docs/source/Concepts/Colors.md +++ b/docs/source/Concepts/Colors.md @@ -1,6 +1,6 @@ # Colors -*Note that the Documentation does not display colour the way it would look on the screen.* +> Note that the Documentation does not display colour the way it would look on the screen. Color can be a very useful tool for your game. It can be used to increase readability and make your game more appealing visually. @@ -130,8 +130,11 @@ For a detailed explanation of these caveats, see the [Understanding Color Tags]( Tags) tutorial. But most of the time you might be better off to simply avoid `|*` and mark your text manually instead. -### Xterm256 Colours +## Xterm256 Colours +```{sidebar} +See the [Understanding Color Tags](../Howtos/Tutorial-Understanding-Color-Tags.md) tutorial, for more on the use of ANSI color tags and the pitfalls of mixing ANSI and Xterms256 color tags in the same context. +``` The _Xterm256_ standard is a colour scheme that supports 256 colours for text and/or background. It can be combined freely with ANSI colors (above), but some ANSI tags don't affect Xterm256 tags. While this offers many more possibilities than traditional ANSI colours, be wary that too many text @@ -170,7 +173,3 @@ If you have a client that supports Xterm256, you can use to get a table of all the 256 colours and the codes that produce them. If the table looks broken up into a few blocks of colors, it means Xterm256 is not supported and ANSI are used as a replacement. You can use the `options` command to see if xterm256 is active for you. This depends on if your client told Evennia what it supports - if not, and you know what your client supports, you may have to activate some features manually. - -## More reading - -There is an [Understanding Color Tags](../Howtos/Tutorial-Understanding-Color-Tags.md) tutorial which expands on the use of ANSI color tags and the pitfalls of mixing ANSI and Xterms256 color tags in the same context. \ No newline at end of file diff --git a/docs/source/Concepts/Concepts-Overview.md b/docs/source/Concepts/Concepts-Overview.md index a4be3f37f3..41b18125be 100644 --- a/docs/source/Concepts/Concepts-Overview.md +++ b/docs/source/Concepts/Concepts-Overview.md @@ -7,12 +7,19 @@ This documentation cover more over-arching concepts of Evennia, often involving ```{toctree} :maxdepth: 2 -Async-Process.md -Soft-Code.md -Using-MUX-as-a-Standard.md Messagepath.md OOB.md +Async-Process.md +``` +## Text processing +```{toctree} +:maxdepth: 2 + +Tags-Parsed-By-Evennia.md +Change-Message-Per-Receiver.md +Internationalization.md +Text-Encodings.md ``` ## Access @@ -21,10 +28,8 @@ OOB.md :maxdepth: 2 Multisession-modes.md -Building-Permissions.md -Guest-Logins.md +Guests.md Banning.md - ``` ## Extending the Server @@ -32,31 +37,7 @@ Banning.md ```{toctree} :maxdepth: 2 -Custom-Protocols.md -Bootstrap-&-Evennia.md -New-Models.md +Protocols.md +Models.md Zones.md - -``` - -## Text processing -```{toctree} -:maxdepth: 2 - -Internationalization.md -Text-Encodings.md -Inline-Tags-and-Functions.md -Change-Messages-Per-Receiver.md -Clickable-Links.md -Colors.md - -``` - - -## Web features -```{toctree} -:maxdepth: 2 - -Web-Features.md -``` - +``` \ No newline at end of file diff --git a/docs/source/Concepts/Guest-Logins.md b/docs/source/Concepts/Guests.md similarity index 100% rename from docs/source/Concepts/Guest-Logins.md rename to docs/source/Concepts/Guests.md diff --git a/docs/source/Concepts/Inline-Functions.md b/docs/source/Concepts/Inline-Functions.md new file mode 100644 index 0000000000..e9ba2c25c9 --- /dev/null +++ b/docs/source/Concepts/Inline-Functions.md @@ -0,0 +1,22 @@ +# Inline functions + +```{sidebar} +For much more information about inline functions, see the [FuncParser](../Components/FuncParser.md) documentation +``` +_Inline functions_, also known as _funcparser functions_ are embedded strings on the form + + $funcname(args, kwargs) + +For example + + > say the answer is $eval(24 * 12)! + You say, "the answer is 288!" + +General processing of outgoing strings is disabled by default. To activate inline-function parsing of outgoing strings, add this to your settings file: + + FUNCPARSER_PARSE_OUTGOING_MESSAGES_ENABLED=True + +Inline functions are provided by the [FuncParser](../Components/FuncParser.md). It is enabled in a few other situations: + +- Processing of [Prototypes](../Components/Prototypes.md); these 'prototypefuncs' allow for prototypes whose values change dynamically upon spawning. For example, you would set `{key: '$choice(["Bo", "Anne", "Tom"])'` and spawn a random-named character every time. +- Processing of strings to the `msg_contents` method. This allows for [sending different messages depending on who will see them](./Change-Message-Per-Receiver.md). \ No newline at end of file diff --git a/docs/source/Concepts/Inline-Tags-and-Functions.md b/docs/source/Concepts/Inline-Tags-and-Functions.md deleted file mode 100644 index 36b44edcf7..0000000000 --- a/docs/source/Concepts/Inline-Tags-and-Functions.md +++ /dev/null @@ -1,18 +0,0 @@ -# In-text tags parsed by Evennia - -Evennia understands various extra information embedded in text: - -- [Colors](./Colors.md) - Using `|r`, `|n` etc can be used to mark parts of text with a color. The color will - become ANSI/XTerm256 color tags for Telnet connections and CSS information for the webclient. -- [Clickable links](./Clickable-Links.md) - This allows you to provide a text the user can click to execute an - in-game command. This is on the form `|lc command |lt text |le`. -- [FuncParser callables](../Components/FuncParser.md) - These are full-fledged function calls on the form `$funcname(args, kwargs)` - that lead to calls to Python functions. The parser can be run with different available callables in different circumstances. The parser is run on all outgoing messages if `settings.FUNCPARSER_PARSE_OUTGOING_MESSAGES_ENABLED=True` (disabled by default). - -```{toctree} -:hidden" - -Colors.md -Clickable-Links.md -../Components/FuncParser.md -``` \ No newline at end of file diff --git a/docs/source/Concepts/Messagepath.md b/docs/source/Concepts/Messagepath.md index 3ab28d46a1..cacd8754b0 100644 --- a/docs/source/Concepts/Messagepath.md +++ b/docs/source/Concepts/Messagepath.md @@ -1,211 +1,192 @@ -# Messagepath +# The Message path +```shell +> look -The main functionality of Evennia is to communicate with clients connected to it; a player enters -commands or their client queries for a gui update (ingoing data). The server responds or sends data -on its own as the game changes (outgoing data). It's important to understand how this flow of -information works in Evennia. +A Meadow -## The ingoing message path +This is a beautiful meadow. It is full of flowers. -We'll start by tracing data from the client to the server. Here it is in short: +You see: a flower +Exits: north, east +``` - Client -> - PortalSession -> - PortalSessionhandler -> - (AMP) -> - ServerSessionHandler -> - ServerSession -> - Inputfunc +When you send a command like `look` into Evennia - what actually happens? How does that `look` string end up being handled by the `CmdLook` class? What happens when we use e.g. `caller.msg()` to send the message back -### Client (ingoing) +Understanding this flow of data - the _message path_ is important in order to understand how Evennia works. -The client sends data to Evennia in two ways. +## Ingoing message path - - When first connecting, the client can send data to the server about its - capabilities. This is things like "I support xterm256 but not unicode" and is - mainly used when a Telnet client connects. This is called a "handshake" and - will generally set some flags on the [Portal Session](../Components/Portal-And-Server.md) that - are later synced to the Server Session. Since this is not something the player - controls, we'll not explore this further here. - - The client can send an *inputcommand* to the server. Traditionally this only - happens when the player enters text on the command line. But with a custom - client GUI, a command could also come from the pressing of a button. Finally - the client may send commands based on a timer or some trigger. +``` + Internet│ + ┌─────┐ │ ┌────────┐ +┌──────┐ │Text │ │ ┌────────────┐ ┌─────────┐ │Command │ +│Client├────┤JSON ├─┼──►commandtuple├────►Inputfunc├────►DB query│ +└──────┘ │etc │ │ └────────────┘ └─────────┘ │etc │ + └─────┘ │ └────────┘ + │Evennia +``` -Exactly how the inputcommand looks when it travels from the client to Evennia -depends on the [Protocol](./Custom-Protocols.md) used: - - Telnet: A string. If GMCP or MSDP OOB protocols are used, this string will - be formatted in a special way, but it's still a raw string. If Telnet SSL is - active, the string will be encrypted. - - SSH: An encrypted string - - Webclient: A JSON-serialized string. +### Incoming command tuples -### Portal Session (ingoing) - -Each client is connected to the game via a *Portal Session*, one per connection. This Session is -different depending on the type of connection (telnet, webclient etc) and thus know how to handle -that particular data type. So regardless of how the data arrives, the Session will identify the type -of the instruction and any arguments it should have. For example, the telnet protocol will figure -that anything arriving normally over the wire should be passed on as a "text" type. - -### PortalSessionHandler (ingoing) - -The *PortalSessionhandler* manages all connected Sessions in the Portal. Its `data_in` method -(called by each Portal Session) will parse the command names and arguments from the protocols and -convert them to a standardized form we call the *inputcommand*: +Ingoing data from the client (coming in as raw strings or serialized JSON) is converted by Evennia to a `commandtuple`. Thesa are the same regardless of what client or connection was used. A `commandtuple` is a simple tuple with three elements: ```python - (commandname, (args), {kwargs}) +(commandname, (args), {kwargs}) ``` -All inputcommands must have a name, but they may or may not have arguments and keyword arguments - -in fact no default inputcommands use kwargs at all. The most common inputcommand is "text", which -has the argument the player input on the command line: +For the `look`-command (and anything else written by the player), the `text` `commandtuple` is generated: ```python - ("text", ("look",), {}) +("text", ("look",), {}) ``` -This inputcommand-structure is pickled together with the unique session-id of the Session to which -it belongs. This is then sent over the AMP connection. +### Inputfuncs -### ServerSessionHandler (ingoing) - -On the Server side, the AMP unpickles the data and associates the session id with the server-side -[Session](../Components/Sessions.md). Data and Session are passed to the server-side `SessionHandler.data_in`. This -in turn calls `ServerSession.data_in()` - -### ServerSession (ingoing) - -The method `ServerSession.data_in` is meant to offer a single place to override if they want to -examine *all* data passing into the server from the client. It is meant to call the -`ssessionhandler.call_inputfuncs` with the (potentially processed) data (so this is technically a -sort of detour back to the sessionhandler). - -In `call_inputfuncs`, the inputcommand's name is compared against the names of all the *inputfuncs* -registered with the server. The inputfuncs are named the same as the inputcommand they are supposed -to handle, so the (default) inputfunc for handling our "look" command is called "text". These are -just normal functions and one can plugin new ones by simply putting them in a module where Evennia -looks for such functions. - -If a matching inputfunc is found, it will be called with the Session and the inputcommand's -arguments: +On the Evennia server side, a list of [inputfucs](Inputuncs) are registered. You can add your own by extending `settings.INPUT_FUNC_MODULES`. ```python - text(session, *("look",), **{}) +inputfunc_commandname(session, *args, **kwargs) ``` +Here the `session` represents the unique client connection this is coming from (that is, it's identifying just _who_ is sending this input). - If no matching inputfunc is found, an inputfunc named "default" will be tried and if that is also -not found, an error will be raised. - -### Inputfunc - -The [Inputfunc](../Components/Inputfuncs.md) must be on the form `func(session, *args, **kwargs)`. An exception is -the `default` inputfunc which has form `default(session, cmdname, *args, **kwargs)`, where `cmdname` -is the un-matched inputcommand string. - -This is where the message's path diverges, since just what happens next depends on the type of -inputfunc was triggered. In the example of sending "look", the inputfunc is named "text". It will -pass the argument to the `cmdhandler` which will eventually lead to the `look` command being -executed. - - -## The outgoing message path - -Next let's trace the passage from server to client. - - msg -> - ServerSession -> - ServerSessionHandler -> - (AMP) -> - PortalSessionHandler -> - PortalSession -> - Client - -### msg - -All outgoing messages start in the `msg` method. This is accessible from three places: - - - `Object.msg` - - `Account.msg` - - `Session.msg` - -The call sign of the `msg` method looks like this: +One such inputfunc is named `text`. For sending a `look`, it will be called as +```{sidebar} +If you know how `*args` and `**kwargs` work in Python, you'll see that this is the same as a call `text(session, "look")` +``` ```python - msg(text=None, from_obj=None, session=None, options=None, **kwargs) +text(session, *("look",), **{}) ``` -For our purposes, what is important to know is that with the exception of `from_obj`, `session` and -`options`, all keywords given to the `msg` method is the name of an *outputcommand* and its -arguments. So `text` is actually such a command, taking a string as its argument. The reason `text` -sits as the first keyword argument is that it's so commonly used (`caller.msg("Text")` for example). -Here are some examples +What an `inputfunc` does with this depends. For an [Out-of-band](./OOB.md) instruction, it could fetch the health of a player or tick down some counter. + +```{sidebar} No text parsing happens before this +If you send `look here`, the call would be `text(session, *("look here", **{})`. All parsing of the text input happens in the command-parser, after this step. +``` +For the `text` `inputfunc` the Evennia [CommandHandler](../Components/Commands.md) is invoked and the argument is parsed further in order to figure which command was intended. + +In the example of `look`, the `CmdLook` command-class will be invoked. This will retrieve the description of the current location. + +## Outgoing message path + +``` + Internet│ + ┌─────┐ │ +┌──────┐ │Text │ │ ┌──────────┐ ┌────────────┐ ┌─────┐ +│Client◄────┤JSON ├─┼──┤outputfunc◄────┤commandtuple◄───┤msg()│ +└──────┘ │etc │ │ └──────────┘ └────────────┘ └─────┘ + └─────┘ │ + │Evennia +``` + +### `msg` to outgoing commandtuple + +When the `inputfunc` has finished whatever it is supposed to, the server may or may not decide to return a result (Some types of `inputcommands` may not expect or require a response at all). The server also often sends outgoing messages without any prior matching ingoing data. + +Whenever data needs to be sent "out" of Evennia, we must generalize it into a (now outgoing) `commandtuple` `(commandname, (args), {kwargs})`. This we do with the `msg()` method. For convenience, this methods is available on every major entity, such as `Object.msg()` and `Account.msg()`. They all link back to `Session.msg()`. ```python - msg("Hello!") # using the 'text' outputfunc - msg(prompt=f"HP: {HP}, SP: {SP}, MP: {MP}") - msg(mycommand=((1,2,3,4), {"foo": "bar"}) - -``` -Note the form of the `mycommand` outputfunction. This explicitly defines the arguments and keyword -arguments for the function. In the case of the `text` and `prompt` calls we just specify a string - -this works too: The system will convert this into a single argument for us later in the message -path. - -> Note: The `msg` method sits on your Object- and Account typeclasses. It means you can easily -override `msg` and make custom- or per-object modifications to the flow of data as it passes -through. - -### ServerSession (outgoing) - -Nothing is processed on the Session, it just serves as a gathering points for all different `msg`. -It immediately passes the data on to ... - -### ServerSessionHandler (outgoing) - -In the *ServerSessionhandler*, the keywords from the `msg` method are collated into one or more -*outputcommands* on a standardized form (identical to inputcommands): - -``` - (commandname, (args), {kwargs}) +msg(text=None, from_obj=None, session=None, options=None, **kwargs) ``` -This will intelligently convert different input to the same form. So `msg("Hello")` will end up as -an outputcommand `("text", ("Hello",), {})`. +`text` is so common that it is given as the default: -This is also the point where the [FuncParser](../Components/FuncParser.md)) is applied, depending on the -session to receive the data. Said data is pickled together with the Session id then sent over the -AMP bridge. +```python +msg("A meadow\n\nThis is a beautiful meadow...") +``` -### PortalSessionHandler (outgoing) +This is converted to a `commandtuple` looking like this: +```python +("text", ("A meadow\n\nThis is a beutiful meadow...",) {}) +``` -After the AMP connection has unpickled the data and paired the session id to the matching -PortalSession, the handler next determines if this Session has a suitable method for handling the -outputcommand. +The `msg()` method allows you to define the `commandtuple` directly, for whatever outgoing instruction you want to find: -The situation is analogous to how inputfuncs work, except that protocols are fixed things that don't -need a plugin infrastructure like the inputfuncs are handled. So instead of an "outputfunc", the -handler looks for methods on the PortalSession with names of the form `send_`. +```python +msg(current_status=(("healthy", "charged"), {"hp": 12, "mp": 20})) +``` -For example, the common sending of text expects a PortalSession method `send_text`. This will be -called as `send_text(*("Hello",), **{})`. If the "prompt" outputfunction was used, send_prompt is -called. In all other cases the `send_default(cmdname, *args, **kwargs)` will be called - this is the -case for all client-custom outputcommands, like when wanting to tell the client to update a graphic -or play a sound. +This will be converted to a `commandtuple` looking like this: -### PortalSession (outgoing) +```python +("current_status", ("healthy", "charged"), {"hp": 12, "mp": 20}) +``` -At this point it is up to the session to convert the command into a form understood by this -particular protocol. For telnet, `send_text` will just send the argument as a string (since that is -what telnet clients expect when "text" is coming). If `send_default` was called (basically -everything that is not traditional text or a prompt), it will pack the data as an GMCP or MSDP -command packet if the telnet client supports either (otherwise it won't send at all). If sending to -the webclient, the data will get packed into a JSON structure at all times. +### outputfuncs -### Client (outgoing) +```{sidebar} +`outputfuncs` are tightly coupled to the protocol and you usually don't need to touch them, unless you are adding a new protocol entirely. +``` +Since `msg()` is aware of which [Session](../Components/Sessions.md) to send to, the outgoing `commandtuple` is always end up pointed at the right client. -Once arrived at the client, the outputcommand is handled in the way supported by the client (or it -may be quietly ignored if not). "text" commands will be displayed in the main window while others -may trigger changes in the GUI or play a sound etc. +Each supported Evennia Protocol (Telnet, SSH, Webclient etc) has their own `outputfunc`, which converts the generic `commandtuple` into a form that particular protocol understands, such as telnet instructions or JSON. + +For telnet (no SSL), the `look` will return over the wire as plain text: + + A meadow\n\nThis is a beautiful meadow... + +When sending to the webclient, the `commandtuple` is converted as serialized JSON, like this: + + '["look", ["A meadow\\n\\nThis is a beautiful meadow..."], {}]' + +This is then sent to the client over the wire. It's then up to the client to interpret and handle the data properly. + + +## Components along the path + +### Ingoing + +``` + ┌──────┐ ┌─────────────────────────┐ + │Client│ │ │ + └──┬───┘ │ ┌────────────────────┐ │ + │ ┌──────┼─►│ServerSessionHandler│ │ +┌──────────────────┼──────┐ │ │ └───┬────────────────┘ │ +│ Portal │ │ │ │ │ │ +│ ┌─────────▼───┐ │ ┌─┴─┐ │ ┌───▼─────────┐ │ +│ │PortalSession│ │ │AMP│ │ │ServerSession│ │ +│ └─────────┬───┘ │ └─┬─┘ │ └───┬─────────┘ │ +│ │ │ │ │ │ │ +│ ┌────────────────▼───┐ │ │ │ ┌───▼─────┐ │ +│ │PortalSessionHandler├──┼──────┘ │ │Inputfunc│ │ +│ └────────────────────┘ │ │ └─────────┘ │ +│ │ │ Server │ +└─────────────────────────┘ └─────────────────────────┘ +``` + +1. Client - sends handshake or commands over the wire. This is received by the Evennia [Portal](../Components/Portal-And-Server.md). +2. `PortalSession` represents one client connection. It understands the communiation protocol used. It converts the protocol-specific input to a generic `commandtuple` structure `(cmdname, (args), {kwargs})`. +3. `PortalSessionHandler` handles all connections. It pickles the `commandtuple` together with the session-id. +4. Pickled data is sent across the `AMP` (Asynchronous Message Protocol) connection to the [Server](Server-And-Portal) part of Evennia. +5. `ServerSessionHandler` unpickles the `commandtuple` and matches the session-id to a matching `SessionSession`. +6. `ServerSession` represents the session-connection on the Server side. It looks through its registry of [Inputfuncs](../Components/Inputfuncs.md) to find a match. +7. The appropriate `Inputfunc` is called with the args/kwargs included in the `commandtuple`. Depending on `Inputfunc`, this could have different effects. For the `text` inputfunc, it fires the [CommandHandler](../Components/Commands.md). + +### Outgoing + +``` + ┌──────┐ ┌─────────────────────────┐ + │Client│ │ │ + └──▲───┘ │ ┌────────────────────┐ │ + │ ┌──────┼──┤ServerSessionHandler│ │ +┌──────────────────┼──────┐ │ │ └───▲────────────────┘ │ +│ Portal │ │ │ │ │ │ +│ ┌─────────┴───┐ │ ┌─┴─┐ │ ┌───┴─────────┐ │ +│ │PortalSession│ │ │AMP│ │ │ServerSession│ │ +│ └─────────▲───┘ │ └─┬─┘ │ └───▲─────────┘ │ +│ │ │ │ │ │ │ +│ ┌────────────────┴───┐ │ │ │ ┌───┴──────┐ │ +│ │PortalSessionHandler◄──┼──────┘ │ │msg() call│ │ +│ └────────────────────┘ │ │ └──────────┘ │ +│ │ │ Server │ +└─────────────────────────┘ └─────────────────────────┘ +``` + +1. The `msg()` method is called +2. `ServerSession` and in particular `ServerSession.msg()` is the central point through which all `msg()` calls are routed in order to send data to that [Session](../Components/Sessions.md). +3. `ServerSessionHandler` converts the `msg` input to a proper `commandtuple` structure `(cmdname, (args), {kwargs})`. It pickles the `commandtuple` together with the session-id. +4. Pickled data is sent across across the `AMP` (Asynchronous Message Protocol) connection to the [Portal](Server-And-Portal) part of Evennia. +5. `PortalSessionHandler` unpickles the `commandtuple` and matches its session id to a matching `PortalSession`. +6. The `PortalSession` is now responsible for converting the generic `commandtuple` to the communication protocol used by that particular connection. +7. The Client receives the data and can act on it. \ No newline at end of file diff --git a/docs/source/Concepts/New-Models.md b/docs/source/Concepts/Models.md similarity index 100% rename from docs/source/Concepts/New-Models.md rename to docs/source/Concepts/Models.md diff --git a/docs/source/Concepts/OOB.md b/docs/source/Concepts/OOB.md index 1e19118eb6..1ba15282b6 100644 --- a/docs/source/Concepts/OOB.md +++ b/docs/source/Concepts/OOB.md @@ -1,155 +1,118 @@ -# OOB +# Out-of-Band messaging OOB, or Out-Of-Band, means sending data between Evennia and the user's client without the user prompting it or necessarily being aware that it's being passed. Common uses would be to update client health-bars, handle client button-presses or to display certain tagged text in a different window pane. -## Briefly on input/outputcommands +If you haven't, you should be familiar with the [Messagepath](./Messagepath.md), which describes how a message enters and leaves Evennia and how along the way, all messages are converted to a generic format called a `commandtuple`: -Inside Evennia, all server-client communication happens in the same way (so plain text is also an -'OOB message' as far as Evennia is concerned). The message follows the [Message Path](./Messagepath.md). -You should read up on that if you are unfamiliar with it. As the message travels along the path it -has a standardized internal form: a tuple with a string, a tuple and a dict: - - ("cmdname", (args), {kwargs}) - -This is often referred to as an *inputcommand* or *outputcommand*, depending on the direction it's -traveling. The end point for an inputcommand, (the 'Evennia-end' of the message path) is a matching -[Inputfunc](../Components/Inputfuncs.md). This function is called as `cmdname(session, *args, **kwargs)` where -`session` is the Session-source of the command. Inputfuncs can easily be added by the developer to -support/map client commands to actions inside Evennia (see the [inputfunc](../Components/Inputfuncs.md) page for more -details). - -When a message is outgoing (at the 'Client-end' of the message path) the outputcommand is handled by -a matching *Outputfunc*. This is responsible for converting the internal Evennia representation to a -form suitable to send over the wire to the Client. Outputfuncs are hard-coded. Which is chosen and -how it processes the outgoing data depends on the nature of the client it's connected to. The only -time one would want to add new outputfuncs is as part of developing support for a new Evennia -[Protocol](./Custom-Protocols.md). + (commandname, (args), {kwargs}) ## Sending and receiving an OOB message -Sending is simple. You just use the normal `msg` method of the object whose session you want to send -to. For example in a Command: +Sending is simple. You just use the normal `msg` method of the object whose session you want to send to. ```python - caller.msg(cmdname=((args, ...), {key:value, ...})) + caller.msg(commandname=((args, ...), {key:value, ...})) ``` -A special case is the `text` input/outputfunc. It's so common that it's the default of the `msg` -method. So these are equivalent: +The keyword becomes the command-name part of the `commandtuple` and the value its `args` and `kwargs` parts. You can also send multiple messages of different `commandname`s at the same time. + +A special case is the `text` call. It's so common that it's the default of the `msg` method. So these are equivalent: ```python caller.msg("Hello") caller.msg(text="Hello") ``` -You don't have to specify the full output/input definition. So for example, if your particular -command only needs kwargs, you can skip the `(args)` part. Like in the `text` case you can skip +You don't have to specify the full `commandtuple` definition. So for example, if your particular command only needs kwargs, you can skip the `(args)` part. Like in the `text` case you can skip writing the tuple if there is only one arg ... and so on - the input is pretty flexible. If there are no args at all you need to give the empty tuple `msg(cmdname=(,)` (giving `None` would mean a single argument `None`). -Which commands you can send depends on the client. If the client does not support an explicit OOB -protocol (like many old/legacy MUD clients) Evennia can only send `text` to them and will quietly -drop any other types of outputfuncs. +### Which command-names can I send? -> Remember that a given message may go to multiple clients with different capabilities. So unless -you turn off telnet completely and only rely on the webclient, you should never rely on non-`text` -OOB messages always reaching all targets. +This depends on the client and protocol. If you use the Evennia [webclient](../Components/Webclient.md), you can modify it to have it support whatever command-names you like. -[Inputfuncs](../Components/Inputfuncs.md) lists the default inputfuncs available to handle incoming OOB messages. To -accept more you need to add more inputfuncs (see that page for more info). +Many third-party MUD clients support a range of OOB protocols listed below. If a client does not support a particular OOB instruction/command, Evennia will just send the `text` command to them and quietly drop all other OOB instructions. +> Note that a given message may go to multiple clients with different capabilities. So unless you turn off telnet completely and only rely on the webclient, you should never rely on non-`text` OOB messages always reaching all targets. + +### Which command-names can I receive + +This is decided by which [Inputfuncs](../Components/Inputfuncs.md) you define. You can extend Evennia's default as you like, but adding your own functions in a module pointed to by `settings.INPUT_FUNC_MODULES`. + ## Supported OOB protocols Evennia supports clients using one of the following protocols: ### Telnet -By default telnet (and telnet+SSL) supports only the plain `text` outputcommand. Evennia however -detects if the Client supports one of two MUD-specific OOB *extensions* to the standard telnet -protocol - GMCP or MSDP. Evennia supports both simultaneously and will switch to the protocol the -client uses. If the client supports both, GMCP will be used. +By default telnet (and telnet+SSL) supports only the plain `text` outputcommand. Evennia detects if the Client supports one of two MUD-specific OOB *extensions* to the standard telnet protocol - GMCP or MSDP. Evennia supports both simultaneously and will switch to the protocol the client uses. If the client supports both, GMCP will be used. -> Note that for Telnet, `text` has a special status as the "in-band" operation. So the `text` -outputcommand sends the `text` argument directly over the wire, without going through the OOB -translations described below. +> Note that for Telnet, `text` has a special status as the "in-band" operation. So the `text` outputcommand sends the `text` argument directly over the wire, without going through the OOB translations described below. #### Telnet + GMCP -[GMCP](https://www.gammon.com.au/gmcp), the *Generic Mud Communication Protocol* sends data on the -form `cmdname + JSONdata`. Here the cmdname is expected to be on the form "Package.Subpackage". -There could also be additional Sub-sub packages etc. The names of these 'packages' and 'subpackages' -are not that well standardized beyond what individual MUDs or companies have chosen to go with over -the years. You can decide on your own package names, but here are what others are using: +[GMCP](https://www.gammon.com.au/gmcp), the *Generic Mud Communication Protocol* sends data on the form `cmdname + JSONdata`. Here the cmdname is expected to be on the form "Package.Subpackage". There could also be additional Sub-sub packages etc. The names of these 'packages' and 'subpackages' are not that well standardized beyond what individual MUDs or companies have chosen to go with over the years. You can decide on your own package names, but here are what others are using: - [Aardwolf GMCP](https://www.aardwolf.com/wiki/index.php/Clients/GMCP) - [Discworld GMCP](https://discworld.starturtle.net/lpc/playing/documentation.c?path=/concepts/gmcp) - [Avatar GMCP](https://www.outland.org/infusions/wiclear/index.php?title=MUD%20Protocols&lang=en) - [IRE games GMCP](https://nexus.ironrealms.com/GMCP) -Evennia will translate underscores to `.` and capitalize to fit the specification. So the -outputcommand `foo_bar` will become a GMCP command-name `Foo.Bar`. A GMCP command "Foo.Bar" will be -come `foo_bar`. To send a GMCP command that turns into an Evennia inputcommand without an -underscore, use the `Core` package. So `Core.Cmdname` becomes just `cmdname` in Evennia and vice -versa. +Evennia will translate underscores to `.` and capitalize to fit the specification. So the outputcommand `foo_bar` will become a GMCP command-name `Foo.Bar`. A GMCP command "Foo.Bar" will be come `foo_bar`. To send a GMCP command that turns into an Evennia inputcommand without an underscore, use the `Core` package. So `Core.Cmdname` becomes just `cmdname` in Evennia and vice versa. -On the wire, a GMCP instruction for `("cmdname", ("arg",), {})` will look like this: +On the wire, the `commandtuple` + + ("cmdname", ("arg",), {}) + +will be sent over the wire as this GMCP telnet instruction IAC SB GMCP "cmdname" "arg" IAC SE -where all the capitalized words are telnet character constants specified in -`evennia/server/portal/telnet_oob.py`. These are parsed/added by the protocol and we don't include -these in the listings below. +where all the capitalized words are telnet character constants specified in ][evennia/server/portal/telnet_oob](evennia.server.portal/telnet_oob.py). These are parsed/added by the protocol and we don't include these in the listings below. -Input/Outputfunc | GMCP-Command -`[cmd_name, [], {}]` | Cmd.Name -`[cmd_name, [arg], {}]` | Cmd.Name arg -`[cmd_na_me, [args],{}]` | Cmd.Na.Me [args] -`[cmd_name, [], {kwargs}]` | Cmd.Name {kwargs} -`[cmdname, [args, {kwargs}]` | Core.Cmdname [[args],{kwargs}] +| `commandtuple` | GMCP-Command | +| --- | ---| +| `(cmd_name, (), {})` | `Cmd.Name` | +| `(cmd_name, (arg,), {})` | `Cmd.Name arg` | +| `(cmd_na_me, (args,...),{})` | `Cmd.Na.Me [arg, arg...]` | +| `(cmd_name, (), {kwargs})` | `Cmd.Name {kwargs}` | +| `(cmdname, (arg,), {kwargs})` | `Core.Cmdname [[args],{kwargs}]` | -Since Evennia already supplies default inputfuncs that don't match the names expected by the most -common GMCP implementations we have a few hard-coded mappings for those: +Since Evennia already supplies default Inputfuncs that don't match the names expected by the most common GMCP implementations we have a few hard-coded mappings for those: -GMCP command name | Input/Outputfunc name -"Core.Hello" | "client_options" -"Core.Supports.Get" | "client_options" -"Core.Commands.Get" | "get_inputfuncs" -"Char.Value.Get" | "get_value" -"Char.Repeat.Update" | "repeat" -"Char.Monitor.Update" | "monitor" +| GMCP command name | `commandtuple` command name | +| --- | --- | +| `"Core.Hello"` | `"client_options"` | +| `"Core.Supports.Get"` | `"client_options"` | +| `"Core.Commands.Get"` | `"get_inputfuncs"` | +| `"Char.Value.Get"` | `"get_value"` | +| `"Char.Repeat.Update"` | `"repeat"` | +| `"Char.Monitor.Update"`| `"monitor"` | #### Telnet + MSDP -[MSDP](http://tintin.sourceforge.net/msdp/), the *Mud Server Data Protocol*, is a competing standard -to GMCP. The MSDP protocol page specifies a range of "recommended" available MSDP command names. -Evennia does *not* support those - since MSDP doesn't specify a special format for its command names -(like GMCP does) the client can and should just call the internal Evennia inputfunc by its actual -name. +[MSDP](http://tintin.sourceforge.net/msdp/), the *Mud Server Data Protocol*, is a competing standard to GMCP. The MSDP protocol page specifies a range of "recommended" available MSDP command names. Evennia does *not* support those - since MSDP doesn't specify a special format for its command names (like GMCP does) the client can and should just call the internal Evennia inputfunc by its actual name. -MSDP uses Telnet character constants to package various structured data over the wire. MSDP supports -strings, arrays (lists) and tables (dicts). These are used to define the cmdname, args and kwargs -needed. When sending MSDP for `("cmdname", ("arg",), {})` the resulting MSDP instruction will look -like this: +MSDP uses Telnet character constants to package various structured data over the wire. MSDP supports strings, arrays (lists) and tables (dicts). These are used to define the cmdname, args and kwargs needed. When sending MSDP for `("cmdname", ("arg",), {})` the resulting MSDP instruction will look like this: IAC SB MSDP VAR cmdname VAL arg IAC SE The various available MSDP constants like `VAR` (variable), `VAL` (value), `ARRAYOPEN`/`ARRAYCLOSE` and `TABLEOPEN`/`TABLECLOSE` are specified in `evennia/server/portal/telnet_oob`. -Outputfunc/Inputfunc | MSDP instruction -`[cmdname, [], {}]` | VAR cmdname VAL -`[cmdname, [arg], {}]` | VAR cmdname VAL arg -`[cmdname, [args],{}]` | VAR cmdname VAL ARRAYOPEN VAL arg VAL arg ... ARRAYCLOSE -`[cmdname, [], {kwargs}]` | VAR cmdname VAL TABLEOPEN VAR key VAL val ... TABLECLOSE -`[cmdname, [args], {kwargs}]` | VAR cmdname VAL ARRAYOPEN VAL arg VAL arg ... ARRAYCLOSE VAR cmdname -VAL TABLEOPEN VAR key VAL val ... TABLECLOSE +| `commandtuple` | MSDP instruction | +| --- | --- | +| `(cmdname, (), {})` | `VAR cmdname VAL` | +| `(cmdname, (arg,), {})` | `VAR cmdname VAL arg` | +| `(cmdname, (arg,...),{})` | `VAR cmdname VAL ARRAYOPEN VAL arg VAL arg ... ARRAYCLOSE` | +| `(cmdname, (), {kwargs})` | `VAR cmdname VAL TABLEOPEN VAR key VAL val ... TABLECLOSE` | +| `(cmdname, (args,...), {kwargs})` | `VAR cmdname VAL ARRAYOPEN VAL arg VAL arg ... ARRAYCLOSE VAR cmdname VAL TABLEOPEN VAR key VAL val ... TABLECLOSE` | -Observe that `VAR ... VAL` always identifies cmdnames, so if there are multiple arrays/dicts tagged -with the same cmdname they will be appended to the args, kwargs of that inputfunc. Vice-versa, a +Observe that `VAR ... VAL` always identifies `cmdnames`, so if there are multiple arrays/dicts tagged with the same cmdname they will be appended to the args, kwargs of that inputfunc. Vice-versa, a different `VAR ... VAL` (outside a table) will come out as a second, different command input. ### SSH @@ -158,10 +121,14 @@ SSH only supports the `text` input/outputcommand. ### Web client -Our web client uses pure JSON structures for all its communication, including `text`. This maps -directly to the Evennia internal output/inputcommand, including eventual empty args/kwargs. So the -same example `("cmdname", ("arg",), {})` will be sent/received as a valid JSON structure +Our web client uses pure [JSON](https://en.wikipedia.org/wiki/JSON) structures for all its communication, including `text`. This maps directly to the Evennia internal output/inputcommand, including eventual empty args/kwargs. - ["cmdname, ["arg"], {}] +| `commandtuple` | Evennia Webclient JSON | +| --- | --- | +| `(cmdname, (), {})` | `["cmdname", [], {}]` | +| `(cmdname, (arg,), {})` | `["cmdname", [arg], {}]` | +| `(cmdname, (arg,...),{})` | `["cmdname", [arg, ...], {})` | +| `(cmdname, (), {kwargs})` | `["cmdname", [], {kwargs})` | +| `(cmdname, (arg,...), {kwargs})` | `["cmdname", [arg, ...], {kwargs})` | -Since JSON is native to Javascript, this becomes very easy for the webclient to handle. +Since JSON is native to Javascript, this becomes very easy for the webclient to handle. \ No newline at end of file diff --git a/docs/source/Concepts/Custom-Protocols.md b/docs/source/Concepts/Protocols.md similarity index 100% rename from docs/source/Concepts/Custom-Protocols.md rename to docs/source/Concepts/Protocols.md diff --git a/docs/source/Concepts/Tags-Parsed-By-Evennia.md b/docs/source/Concepts/Tags-Parsed-By-Evennia.md new file mode 100644 index 0000000000..7dd96c72f1 --- /dev/null +++ b/docs/source/Concepts/Tags-Parsed-By-Evennia.md @@ -0,0 +1,27 @@ +# In-text tags parsed by Evennia +```{toctree} +:maxdepth: 2 + +Colors.md +Clickable-Links.md +Inline-Functions.md +``` + +Evennia will parse various special tags and markers embedded in text and convert it dynamically depending on if the data is going in or out of the server. + +- _Colors_ - Using `|r`, `|n` etc can be used to mark parts of text with a color. The color will + become ANSI/XTerm256 color tags for Telnet connections and CSS information for the webclient. + ``` + > say Hello, I'm wearing my |rred hat|n today. + ``` +- _Clickable links_ - This allows you to provide a text the user can click to execute an + in-game command. This is on the form `|lc command |lt text |le`. Clickable links are generally only parsed in the _outgoing_ direction, since if users could provde them, they could be a potential security problem. To activate, `MXP_ENABLED=True` must be added to settings (disabled by default). + ``` + py self.msg("This is a |c look |ltclickable 'look' link|le") + ``` +- _FuncParser callables_ - These are full-fledged function calls on the form `$funcname(args, kwargs)` that lead to calls to Python functions. The parser can be run with different available callables in different circumstances. The parser is run on all outgoing messages if `settings.FUNCPARSER_PARSE_OUTGOING_MESSAGES_ENABLED=True` (disabled by default). + ``` + > say The answer is $eval(40 + 2)! + ``` + + diff --git a/docs/source/Concepts/Using-MUX-as-a-Standard.md b/docs/source/Concepts/Using-MUX-as-a-Standard.md deleted file mode 100644 index ddb35adac6..0000000000 --- a/docs/source/Concepts/Using-MUX-as-a-Standard.md +++ /dev/null @@ -1,12 +0,0 @@ -# Using MUX as a Standard - - -Evennia allows for any command syntax. If you like the way DikuMUDs, LPMuds or MOOs handle things, you could emulate that with Evennia. If you are ambitious you could even design a whole new style, perfectly fitting your own dreams of the ideal game. - -We do offer a default however. The default Evennia setup tends to *resemble* [MUX2](https://www.tinymux.org/), and its cousins [PennMUSH](https://www.pennmush.org), [TinyMUSH](https://github.com/TinyMUSH/TinyMUSH/wiki), and [RhostMUSH](http://www.rhostmush.com/). While the reason for this similarity is partly historical, these codebases offer very mature feature sets for administration and building. - -Evennia is *not* a MUX system though. It works very differently in many ways. For example, Evennia -deliberately lacks an online softcode language (a policy explained on our [softcode policy -page](./Soft-Code.md)). Evennia also does not shy from using its own syntax when deemed appropriate: the -MUX syntax has grown organically over a long time and is, frankly, rather arcane in places. All in -all the default command syntax should at most be referred to as "MUX-like" or "MUX-inspired". \ No newline at end of file diff --git a/docs/source/Concepts/Web-Features.md b/docs/source/Concepts/Web-Features.md deleted file mode 100644 index 7263371e7e..0000000000 --- a/docs/source/Concepts/Web-Features.md +++ /dev/null @@ -1,133 +0,0 @@ -# Web Features - - -Evennia is its own webserver and hosts a default website and browser webclient. - -## Web site - -The Evennia website is a Django application that ties in with the MUD database. Since the website -shares this database you could, for example, tell website visitors how many accounts are logged into -the game at the moment, how long the server has been up and any other database information you may -want. During development you can access the website by pointing your browser to -`http://localhost:4001`. - -> You may also want to set `DEBUG = True` in your settings file for debugging the website. You will -then see proper tracebacks in the browser rather than just error codes. Note however that this will -*leak memory a lot* (it stores everything all the time) and is *not to be used in production*. It's -recommended to only use `DEBUG` for active web development and to turn it off otherwise. - -A Django (and thus Evennia) website basically consists of three parts, a -[view](https://docs.djangoproject.com/en/1.9/topics/http/views/) an associated -[template](https://docs.djangoproject.com/en/1.9/topics/templates/) and an `urls.py` file. Think of -the view as the Python back-end and the template as the HTML files you are served, optionally filled -with data from the back-end. The urls file is a sort of mapping that tells Django that if a specific -URL is given in the browser, a particular view should be triggered. You are wise to review the -Django documentation for details on how to use these components. - -Evennia's default website is located in -[evennia/web/website](https://github.com/evennia/evennia/tree/master/evennia/web/website). In this -folder you'll find the simple default view as well as subfolders `templates` and `static`. Static -files are things like images, CSS files and Javascript. - -### Customizing the Website - -You customize your website from your game directory. In the folder `web` you'll find folders -`static`, `templates`, `static_overrides` and `templates_overrides`. The first two of those are -populated automatically by Django and used to serve the website. You should not edit anything in -them - the change will be lost. To customize the website you'll need to copy the file you want to -change from the `web/website/template/` or `web/website/static/ path to the corresponding place -under one of `_overrides` directories. - -Example: To override or modify `evennia/web/website/template/website/index.html` you need to -add/modify `mygame/web/template_overrides/website/index.html`. - -The detailed description on how to customize the website is best described in tutorial form. See the -[Web Tutorial](../Howtos/Web-Changing-Webpage.md) for more information. - -### Overloading Django views - -The Python backend for every HTML page is called a [Django -view](https://docs.djangoproject.com/en/1.9/topics/http/views/). A view can do all sorts of -functions, but the main one is to update variables data that the page can display, like how your -out-of-the-box website will display statistics about number of users and database objects. - -To re-point a given page to a `view.py` of your own, you need to modify `mygame/web/urls.py`. An -[URL pattern](https://docs.djangoproject.com/en/1.9/topics/http/urls/) is a [regular -expression](https://en.wikipedia.org/wiki/Regular_expression) that you need to enter in the address -field of your web browser to get to the page in question. If you put your own URL pattern *before* -the default ones, your own view will be used instead. The file `urls.py` even marks where you should -put your change. - -Here's an example: - -```python -# mygame/web/urls.py - -from django.conf.urls import url, include -# default patterns -from evennia.web.urls import urlpatterns - -# our own view to use as a replacement -from web.myviews import myview - -# custom patterns to add -patterns = [ - # overload the main page view - url(r'^', myview, name='mycustomview'), -] - -urlpatterns = patterns + urlpatterns - -``` - -Django will always look for a list named `urlpatterns` which consists of the results of `url()` -calls. It will use the *first* match it finds in this list. Above, we add a new URL redirect from -the root of the website. It will now our own function `myview` from a new module -`mygame/web/myviews.py`. - -> If our game is found on `http://mygame.com`, the regular expression `"^"` means we just entered -`mygame.com` in the address bar. If we had wanted to add a view for `http://mygame.com/awesome`, the -regular expression would have been `^/awesome`. - -Look at -[evennia/web/website/views.py](https://github.com/evennia/evennia/blob/master/evennia/web/website/views.py#L82) -to see the inputs and outputs you must have to define a view. Easiest may be to copy the default -file to `mygame/web` to have something to modify and expand on. - -Restart the server and reload the page in the browser - the website will now use your custom view. -If there are errors, consider turning on `settings.DEBUG` to see the full tracebacks - in debug mode -you will also log all requests in `mygame/server/logs/http_requests.log`. - -## Web client - - -Evennia comes with a MUD client accessible from a normal web browser. During -development you can try it at `http://localhost:4001/webclient`. -[See the Webclient page](../Components/Webclient.md) for more details. - - -## The Django 'Admin' Page - -Django comes with a built-in [admin -website](https://docs.djangoproject.com/en/1.10/ref/contrib/admin/). This is accessible by clicking -the 'admin' button from your game website. The admin site allows you to see, edit and create objects -in your database from a graphical interface. - -The behavior of default Evennia models are controlled by files `admin.py` in the Evennia package. -New database models you choose to add yourself (such as in the Web Character View Tutorial) can/will -also have `admin.py` files. New models are registered to the admin website by a call of -`admin.site.register(model class, admin class)` inside an admin.py file. It is an error to attempt -to register a model that has already been registered. - -To overload Evennia's admin files you don't need to modify Evennia itself. To customize you can call -`admin.site.unregister(model class)`, then follow that with `admin.site.register` in one of your own -admin.py files in a new app that you add. - -## More reading - -Evennia relies on Django for its web features. For details on expanding your web experience, the -[Django documentation](https://docs.djangoproject.com/en) or the [Django -Book](http://www.djangobook.com/en/2.0/index.html) are the main resources to look into. In Django -lingo, the Evennia is a django "project" that consists of Django "applications". For the sake of web -implementation, the relevant django "applications" in default Evennia are `web/website` or -`web/webclient`. \ No newline at end of file diff --git a/docs/source/Evennia-Introduction.md b/docs/source/Evennia-Introduction.md index 5554b24475..22556c53d2 100644 --- a/docs/source/Evennia-Introduction.md +++ b/docs/source/Evennia-Introduction.md @@ -29,7 +29,7 @@ We also include a growing list of *optional* [contribs](Contribs/Contribs-Overvi Using the full power of Python throughout the server offers some distinct advantages. All your coding, from object definitions and custom commands to AI scripts and economic systems is done in normal Python modules rather than some ad-hoc scripting language. The fact that you script the game in the same high-level language that you code it in allows for very powerful and custom game implementations indeed. -Out of the box, Evennia gives you a 'talker'-type of game; you can walk around, chat, build rooms and objects, do basic roleplaying and administration. The server ships with a default set of player commands that are similar to the MUX command set. We *do not* aim specifically to be a MUX server, but we had to pick some default to go with (see [this](Concepts/Soft-Code.md) for more about our original motivations). It's easy to remove or add commands, or to have the command syntax mimic other systems, like Diku, LP, MOO and so on. Or why not create a new and better command system of your own design. +Out of the box, Evennia gives you a 'talker'-type of game; you can walk around, chat, build rooms and objects, do basic roleplaying and administration. The server ships with a default set of player commands that are similar to the MUX command set. We *do not* aim specifically to be a MUX server, but we had to pick some default to go with (see [this](Coding/Soft-Code.md) for more about our original motivations). It's easy to remove or add commands, or to have the command syntax mimic other systems, like Diku, LP, MOO and so on. Or why not create a new and better command system of your own design. ## Can I test it somewhere? diff --git a/docs/source/Howtos/Beginner-Tutorial/Part1/Beginner-Tutorial-Building-Quickstart.md b/docs/source/Howtos/Beginner-Tutorial/Part1/Beginner-Tutorial-Building-Quickstart.md index 07e074a114..fea2acb1a2 100644 --- a/docs/source/Howtos/Beginner-Tutorial/Part1/Beginner-Tutorial-Building-Quickstart.md +++ b/docs/source/Howtos/Beginner-Tutorial/Part1/Beginner-Tutorial-Building-Quickstart.md @@ -7,7 +7,7 @@ the defaults can be quite useful. Connect and log into your new game and you will end up in the "Limbo" location. This is the only room in the game at this point. Let's explore the commands a little. -The default commands has syntax [similar to MUX](../../../Concepts/Using-MUX-as-a-Standard.md): +The default commands has syntax [similar to MUX](../../../Coding/Default-Command-Syntax.md): command[/switch/switch...] [arguments ...] diff --git a/docs/source/Howtos/Beginner-Tutorial/Part2/Beginner-Tutorial-Planning-The-Tutorial-Game.md b/docs/source/Howtos/Beginner-Tutorial/Part2/Beginner-Tutorial-Planning-The-Tutorial-Game.md index 48b87b4777..e7684cafbb 100644 --- a/docs/source/Howtos/Beginner-Tutorial/Part2/Beginner-Tutorial-Planning-The-Tutorial-Game.md +++ b/docs/source/Howtos/Beginner-Tutorial/Part2/Beginner-Tutorial-Planning-The-Tutorial-Game.md @@ -109,7 +109,7 @@ In some game styles, players have the ability to create objects and even script Regular, untrusted users should never be allowed to execute raw Python code (such as what you can do with the `py` command). You can -[read more about Evennia's stance on softcode here](../../../Concepts/Soft-Code.md). If you want users to do limited scripting, it's suggested that this is accomplished by adding more powerful build-commands for them to use. +[read more about Evennia's stance on softcode here](../../../Coding/Soft-Code.md). If you want users to do limited scripting, it's suggested that this is accomplished by adding more powerful build-commands for them to use. **EvAdventure Answer** diff --git a/docs/source/Howtos/Evennia-for-roleplaying-sessions.md b/docs/source/Howtos/Evennia-for-roleplaying-sessions.md index a1d22f3ae5..4320ff7a6c 100644 --- a/docs/source/Howtos/Evennia-for-roleplaying-sessions.md +++ b/docs/source/Howtos/Evennia-for-roleplaying-sessions.md @@ -28,26 +28,19 @@ defaults for our particular use-case. Below we will flesh out these components f We will assume you start from scratch. You need Evennia installed, as per the [Setup Quickstart](../Setup/Installation.md) instructions. Initialize a new game directory with `evennia init -`. In this tutorial we assume your game dir is simply named `mygame`. You can use the -default database and keep all other settings to default for now. Familiarize yourself with the -`mygame` folder before continuing. You might want to browse the -[First Steps Coding](Beginner-Tutorial/Part1/Beginner-Tutorial-Part1-Overview.md) tutorial, just to see roughly where things are modified. +``. In this tutorial we assume your game dir is simply named `mygame`. You can use the default database and keep all other settings to default for now. Familiarize yourself with the +`mygame` folder before continuing. You might want to browse the [Beginner Tutorial](Beginner-Tutorial/Part1/Beginner-Tutorial-Part1-Overview.md) tutorial, just to see roughly where things are modified. ## The Game Master role In brief: -* Simplest way: Being an admin, just give one account `Admins` permission using the standard `@perm` -command. -* Better but more work: Make a custom command to set/unset the above, while tweaking the Character -to show your renewed GM status to the other accounts. +* Simplest way: Being an admin, just give one account `Admins` permission using the standard `perm` command. +* Better but more work: Make a custom command to set/unset the above, while tweaking the Character to show your renewed GM status to the other accounts. ### The permission hierarchy -Evennia has the following [permission hierarchy](../Concepts/Building-Permissions.md#assigning-permissions) out of -the box: *Players, Helpers, Builders, Admins* and finally *Developers*. We could change these but -then we'd need to update our Default commands to use the changes. We want to keep this simple, so -instead we map our different roles on top of this permission ladder. +Evennia has the following [permission hierarchy](../Components/Permissions.md) out of the box: *Players, Helpers, Builders, Admins* and finally *Developers*. We could change these but then we'd need to update our Default commands to use the changes. We want to keep this simple, so instead we map our different roles on top of this permission ladder. 1. `Players` is the permission set on normal players. This is the default for anyone creating a new account on the server. @@ -60,8 +53,7 @@ everyone. 5. `Developers`-level permission are the server administrators, the ones with the ability to restart/shutdown the server as well as changing the permission levels. -> The [superuser](../Concepts/Building-Permissions.md#the-super-user) is not part of the hierarchy and actually -completely bypasses it. We'll assume server admin(s) will "just" be Developers. +> The _superuser_ is not part of the hierarchy and actually completely bypasses it. We'll assume server admin(s) will "just" be Developers. ### How to grant permissions @@ -69,15 +61,15 @@ Only `Developers` can (by default) change permission level. Only they have acces command: ``` -> @perm Yvonne +> perm Yvonne Permissions on Yvonne: accounts -> @perm Yvonne = Admins -> @perm Yvonne +> perm Yvonne = Admins +> perm Yvonne Permissions on Yvonne: accounts, admins -> @perm/del Yvonne = Admins -> @perm Yvonne +> perm/del Yvonne = Admins +> perm Yvonne Permissions on Yvonne: accounts ``` @@ -88,7 +80,7 @@ and singular, so "Admins" gives the same powers as "Admin". ### Optional: Making a GM-granting command -Use of `@perm` works out of the box, but it's really the bare minimum. Would it not be nice if other +Use of `perm` works out of the box, but it's really the bare minimum. Would it not be nice if other accounts could tell at a glance who the GM is? Also, we shouldn't really need to remember that the permission level is called "Admins". It would be easier if we could just do `@gm ` and `@notgm ` and at the same time change something make the new GM status apparent. @@ -221,16 +213,12 @@ class CmdMakeGM(default_cmds.MuxCommand): ``` All the command does is to locate the account target and assign it the `Admins` permission if we -used `@gm` or revoke it if using the `@ungm` alias. We also set/unset the `is_gm` Attribute that is +used `gm` or revoke it if using the `ungm` alias. We also set/unset the `is_gm` Attribute that is expected by our new `Character.get_display_name` method from earlier. -> We could have made this into two separate commands or opted for a syntax like `@gm/revoke -`. Instead we examine how this command was called (stored in `self.cmdstring`) in order -to act accordingly. Either way works, practicality and coding style decides which to go with. +> We could have made this into two separate commands or opted for a syntax like `gm/revoke `. Instead we examine how this command was called (stored in `self.cmdstring`) in order to act accordingly. Either way works, practicality and coding style decides which to go with. -To actually make this command available (only to Developers, due to the lock on it), we add it to -the default Account command set. Open the file `mygame/commands/default_cmdsets.py` and find the -`AccountCmdSet` class: +To actually make this command available (only to Developers, due to the lock on it), we add it to the default Account command set. Open the file `mygame/commands/default_cmdsets.py` and find the `AccountCmdSet` class: ```python # mygame/commands/default_cmdsets.py @@ -246,8 +234,8 @@ class AccountCmdSet(default_cmds.AccountCmdSet): ``` -Finally, issue the `@reload` command to update the server to your changes. Developer-level players -(or the superuser) should now have the `@gm/@ungm` command available. +Finally, issue the `reload` command to update the server to your changes. Developer-level players +(or the superuser) should now have the `gm/ungm` command available. ## Character sheet @@ -260,18 +248,13 @@ In brief: ### Building a Character sheet -There are many ways to build a Character sheet in text, from manually pasting strings together to -more automated ways. Exactly what is the best/easiest way depends on the sheet one tries to create. -We will here show two examples using the *EvTable* and *EvForm* utilities.Later we will create -Commands to edit and display the output from those utilities. +There are many ways to build a Character sheet in text, from manually pasting strings together to more automated ways. Exactly what is the best/easiest way depends on the sheet one tries to create. We will here show two examples using the *EvTable* and *EvForm* utilities.Later we will create Commands to edit and display the output from those utilities. -> Note that due to the limitations of the wiki, no color is used in any of the examples. See -> [the text tag documentation](../Concepts/Inline-Tags-and-Functions.md) for how to add color to the tables and forms. +> Note that these docs don't show the color. see [the text tag documentation](../Concepts/Tags-Parsed-By-Evennia.md) for how to add color to the tables and forms. #### Making a sheet with EvTable -[EvTable](github:evennia.utils.evtable) is a text-table generator. It helps with displaying text in -ordered rows and columns. This is an example of using it in code: +[EvTable](../Components/EvTable.md) is a text-table generator. It helps with displaying text in ordered rows and columns. This is an example of using it in code: ````python # this can be tried out in a Python shell like iPython @@ -288,13 +271,7 @@ table = evtable.EvTable("Attr", "Value", ], align='r', border="incols") ```` -Above, we create a two-column table by supplying the two columns directly. We also tell the table to -be right-aligned and to use the "incols" border type (borders drawns only in between columns). The -`EvTable` class takes a lot of arguments for customizing its look, you can see [some of the possible -keyword arguments here](github:evennia.utils.evtable#evtable__init__). Once you have the `table` you -could also retroactively add new columns and rows to it with `table.add_row()` and -`table.add_column()`: if necessary the table will expand with empty rows/columns to always remain -rectangular. +Above, we create a two-column table by supplying the two columns directly. We also tell the table to be right-aligned and to use the "incols" border type (borders drawns only in between columns). The `EvTable` class takes a lot of arguments for customizing its look, you can see [some of the possible keyword arguments here](github:evennia.utils.evtable#evtable__init__). Once you have the `table` you could also retroactively add new columns and rows to it with `table.add_row()` and `table.add_column()`: if necessary the table will expand with empty rows/columns to always remain rectangular. The result from printing the above table will be @@ -319,10 +296,7 @@ advanced layouts we'll look into EvForm next. #### Making a sheet with EvForm -[EvForm](github:evennia.utils.evform) allows the creation of a two-dimensional "graphic" made by -text characters. On this surface, one marks and tags rectangular regions ("cells") to be filled with -content. This content can be either normal strings or `EvTable` instances (see the previous section, -one such instance would be the `table` variable in that example). +[EvForm](../Components/EvForm.md) allows the creation of a two-dimensional "graphic" made by text characters. On this surface, one marks and tags rectangular regions ("cells") to be filled with content. This content can be either normal strings or `EvTable` instances (see the previous section, one such instance would be the `table` variable in that example). In the case of a Character sheet, these cells would be comparable to a line or box where you could enter the name of your character or their strength score. EvMenu also easily allows to update the @@ -368,13 +342,9 @@ FORM = """ The `#coding` statement (which must be put on the very first line to work) tells Python to use the utf-8 encoding for the file. Using the `FORMCHAR` and `TABLECHAR` we define what single-character we want to use to "mark" the regions of the character sheet holding cells and tables respectively. -Within each block (which must be separated from one another by at least one non-marking character) -we embed identifiers 1-4 to identify each block. The identifier could be any single character except -for the `FORMCHAR` and `TABLECHAR` +Within each block (which must be separated from one another by at least one non-marking character) we embed identifiers 1-4 to identify each block. The identifier could be any single character except for the `FORMCHAR` and `TABLECHAR` -> You can still use `FORMCHAR` and `TABLECHAR` elsewhere in your sheet, but not in a way that it -would identify a cell/table. The smallest identifiable cell/table area is 3 characters wide -including the identifier (for example `x2x`). +> You can still use `FORMCHAR` and `TABLECHAR` elsewhere in your sheet, but not in a way that it would identify a cell/table. The smallest identifiable cell/table area is 3 characters wide including the identifier (for example `x2x`). Now we will map content to this form. @@ -402,10 +372,7 @@ form.map(cells={"1":NAME, "3": ADVANTAGES, "4": DISADVANTAGES}, We create some RP-sounding input and re-use the `table` variable from the previous `EvTable` example. -> Note, that if you didn't want to create the form in a separate module you *could* also load it -directly into the `EvForm` call like this: `EvForm(form={"FORMCHAR":"x", "TABLECHAR":"c", "FORM": -formstring})` where `FORM` specifies the form as a string in the same way as listed in the module -above. Note however that the very first line of the `FORM` string is ignored, so start with a `\n`. +> Note, that if you didn't want to create the form in a separate module you *could* also load it directly into the `EvForm` call like this: `EvForm(form={"FORMCHAR":"x", "TABLECHAR":"c", "FORM": formstring})` where `FORM` specifies the form as a string in the same way as listed in the module above. Note however that the very first line of the `FORM` string is ignored, so start with a `\n`. We then map those to the cells of the form: @@ -432,15 +399,11 @@ print(form) +--------------------------------------+ ```` -As seen, the texts and tables have been slotted into the text areas and line breaks have been added -where needed. We chose to just enter the Advantages/Disadvantages as plain strings here, meaning -long names ended up split between rows. If we wanted more control over the display we could have -inserted `\n` line breaks after each line or used a borderless `EvTable` to display those as well. +As seen, the texts and tables have been slotted into the text areas and line breaks have been added where needed. We chose to just enter the Advantages/Disadvantages as plain strings here, meaning long names ended up split between rows. If we wanted more control over the display we could have inserted `\n` line breaks after each line or used a borderless `EvTable` to display those as well. ### Tie a Character sheet to a Character -We will assume we go with the `EvForm` example above. We now need to attach this to a Character so -it can be modified. For this we will modify our `Character` class a little more: +We will assume we go with the `EvForm` example above. We now need to attach this to a Character so it can be modified. For this we will modify our `Character` class a little more: ```python # mygame/typeclasses/character.py @@ -486,13 +449,13 @@ class Character(DefaultCharacter): ``` -Use `@reload` to make this change available to all *newly created* Characters. *Already existing* +Use `reload` to make this change available to all *newly created* Characters. *Already existing* Characters will *not* have the charsheet defined, since `at_object_creation` is only called once. The easiest to force an existing Character to re-fire its `at_object_creation` is to use the -`@typeclass` command in-game: +`typeclass` command in-game: ``` -@typeclass/force +typeclass/force ``` ### Command for Account to change Character sheet @@ -574,16 +537,13 @@ class CmdSheet(MuxCommand): ``` -Most of this command is error-checking to make sure the right type of data was input. Note how the -`sheet_locked` Attribute is checked and will return if not set. +Most of this command is error-checking to make sure the right type of data was input. Note how the `sheet_locked` Attribute is checked and will return if not set. -This command you import into `mygame/commands/default_cmdsets.py` and add to the `CharacterCmdSet`, -in the same way the `@gm` command was added to the `AccountCmdSet` earlier. +This command you import into `mygame/commands/default_cmdsets.py` and add to the `CharacterCmdSet`, in the same way the `@gm` command was added to the `AccountCmdSet` earlier. ### Commands for GM to change Character sheet -Game masters use basically the same input as Players do to edit a character sheet, except they can -do it on other players than themselves. They are also not stopped by any `sheet_locked` flags. +Game masters use basically the same input as Players do to edit a character sheet, except they can do it on other players than themselves. They are also not stopped by any `sheet_locked` flags. ```python # continuing in mygame/commands/command.py @@ -661,18 +621,13 @@ class CmdGMsheet(MuxCommand): caller.msg(character.db.charsheet) ``` -The `@gmsheet` command takes an additional argument to specify which Character's character sheet to -edit. It also takes `/lock` and `/unlock` switches to block the Player from tweaking their sheet. +The `gmsheet` command takes an additional argument to specify which Character's character sheet to edit. It also takes `/lock` and `/unlock` switches to block the Player from tweaking their sheet. -Before this can be used, it should be added to the default `CharacterCmdSet` in the same way as the -normal `@sheet`. Due to the lock set on it, this command will only be available to `Admins` (i.e. -GMs) or higher permission levels. +Before this can be used, it should be added to the default `CharacterCmdSet` in the same way as the normal `sheet`. Due to the lock set on it, this command will only be available to `Admins` (i.e. GMs) or higher permission levels. ## Dice roller -Evennia's *contrib* folder already comes with a full dice roller. To add it to the game, simply -import `contrib.dice.CmdDice` into `mygame/commands/default_cmdsets.py` and add `CmdDice` to the -`CharacterCmdset` as done with other commands in this tutorial. After a `@reload` you will be able +Evennia's *contrib* folder already comes with a full dice roller. To add it to the game, simply import `contrib.dice.CmdDice` into `mygame/commands/default_cmdsets.py` and add `CmdDice` to the `CharacterCmdset` as done with other commands in this tutorial. After a `@reload` you will be able to roll dice using normal RPG-style format: ``` @@ -680,40 +635,30 @@ roll 2d6 + 3 7 ``` -Use `help dice` to see what syntax is supported or look at `evennia/contrib/dice.py` to see how it's -implemented. +Use `help dice` to see what syntax is supported or look at `evennia/contrib/dice.py` to see how it's implemented. ## Rooms -Evennia comes with rooms out of the box, so no extra work needed. A GM will automatically have all -needed building commands available. A fuller go-through is found in the [Building tutorial](Beginner-Tutorial/Part1/Beginner-Tutorial-Building-Quickstart.md). +Evennia comes with rooms out of the box, so no extra work needed. A GM will automatically have all needed building commands available. A fuller go-through is found in the [Building tutorial](Beginner-Tutorial/Part1/Beginner-Tutorial-Building-Quickstart.md). Here are some useful highlights: -* `@dig roomname;alias = exit_there;alias, exit_back;alias` - this is the basic command for digging -a new room. You can specify any exit-names and just enter the name of that exit to go there. -* `@tunnel direction = roomname` - this is a specialized command that only accepts directions in the -cardinal directions (n,ne,e,se,s,sw,w,nw) as well as in/out and up/down. It also automatically -builds "matching" exits back in the opposite direction. -* `@create/drop objectname` - this creates and drops a new simple object in the current location. -* `@desc obj` - change the look-description of the object. -* `@tel object = location` - teleport an object to a named location. -* `@search objectname` - locate an object in the database. +* `dig roomname;alias = exit_there;alias, exit_back;alias` - this is the basic command for digging a new room. You can specify any exit-names and just enter the name of that exit to go there. +* `tunnel direction = roomname` - this is a specialized command that only accepts directions in the cardinal directions (n,ne,e,se,s,sw,w,nw) as well as in/out and up/down. It also automatically builds "matching" exits back in the opposite direction. +* `create/drop objectname` - this creates and drops a new simple object in the current location. +* `desc obj` - change the look-description of the object. +* `tel object = location` - teleport an object to a named location. +* `search objectname` - locate an object in the database. -> TODO: Describe how to add a logging room, that logs says and poses to a log file that people can -access after the fact. +> TODO: Describe how to add a logging room, that logs says and poses to a log file that people can access after the fact. ## Channels -Evennia comes with [Channels](../Components/Channels.md) in-built and they are described fully in the -documentation. For brevity, here are the relevant commands for normal use: +Evennia comes with [Channels](../Components/Channels.md) in-built and they are described fully in the documentation. For brevity, here are the relevant commands for normal use: -* `@ccreate new_channel;alias;alias = short description` - Creates a new channel. -* `addcom channel` - join an existing channel. Use `addcom alias = channel` to add a new alias you -can use to talk to the channel, as many as desired. -* `delcom alias or channel` - remove an alias from a channel or, if the real channel name is given, -unsubscribe completely. -* `@channels` lists all available channels, including your subscriptions and any aliases you have -set up for them. +* `channel/create = new_channel;alias;alias = short description` - Creates a new channel. +* `channel/sub channel` - subscribe to a channel. +* `channel/unsub channel` - unsubscribel from a channel. +* `channels` lists all available channels, including your subscriptions and any aliases you have set up for them. You can read channel history: if you for example are chatting on the `public` channel you can do `public/history` to see the 20 last posts to that channel or `public/history 32` to view twenty @@ -721,7 +666,7 @@ posts backwards, starting with the 32nd from the end. ## PMs -To send PMs to one another, players can use the `@page` (or `tell`) command: +To send PMs to one another, players can use the `page` (or `tell`) command: ``` page recipient = message diff --git a/docs/source/Howtos/Implementing-a-game-rule-system.md b/docs/source/Howtos/Implementing-a-game-rule-system.md index cc197b9be4..6eb5c3a287 100644 --- a/docs/source/Howtos/Implementing-a-game-rule-system.md +++ b/docs/source/Howtos/Implementing-a-game-rule-system.md @@ -50,7 +50,7 @@ choose to either store things as individual [Attributes](../Components/Attribute dictionary `character.db.skills = {"Hunting":34, "Fishing":20, ...}`. A much more fancy solution is to look at the Ainneve [Trait handler](https://github.com/evennia/ainneve/blob/master/world/traits.py). Finally you could even go -with a [custom django model](../Concepts/New-Models.md). Which is the better depends on your game and the +with a [custom django model](../Concepts/Models.md). Which is the better depends on your game and the complexity of your system. - Make a clear [API](https://en.wikipedia.org/wiki/Application_programming_interface) into your rules. That is, make methods/functions that you feed with, say, your Character and which skill you diff --git a/docs/source/Howtos/Web-Changing-Webpage.md b/docs/source/Howtos/Web-Changing-Webpage.md index d8741641f1..6ad31ba66c 100644 --- a/docs/source/Howtos/Web-Changing-Webpage.md +++ b/docs/source/Howtos/Web-Changing-Webpage.md @@ -11,7 +11,7 @@ Django projects are split up into *apps* and these apps all contribute to one pr you might have an app for conducting polls, or an app for showing news posts or, like us, one for creating a web client. -Each of these applications has a `urls.py` file, which specifies what [URL](https://en.wikipedia.org/wiki/Uniform_resource_locator)s are used by the app, a `views.py` file for the code that the URLs activate, a `templates` directory for displaying the results of that code in [HTML](https://en.wikipedia.org/wiki/Html) for the user, and a `static` folder that holds assets like [CSS](https://en.wikipedia.org/wiki/CSS), [Javascript](https://en.wikipedia.org/wiki/Javascript), and Image files (You may note your mygame/web folder does not have a `static` or `template` folder. This is intended and explained further below). Django applications may also have a `models.py` file for storing information in the database. We will not change any models here, take a look at the [New Models](../Concepts/New-Models.md) page (as well as the [Django docs](https://docs.djangoproject.com/en/1.7/topics/db/models/) on models) if you are interested. +Each of these applications has a `urls.py` file, which specifies what [URL](https://en.wikipedia.org/wiki/Uniform_resource_locator)s are used by the app, a `views.py` file for the code that the URLs activate, a `templates` directory for displaying the results of that code in [HTML](https://en.wikipedia.org/wiki/Html) for the user, and a `static` folder that holds assets like [CSS](https://en.wikipedia.org/wiki/CSS), [Javascript](https://en.wikipedia.org/wiki/Javascript), and Image files (You may note your mygame/web folder does not have a `static` or `template` folder. This is intended and explained further below). Django applications may also have a `models.py` file for storing information in the database. We will not change any models here, take a look at the [New Models](../Concepts/Models.md) page (as well as the [Django docs](https://docs.djangoproject.com/en/1.7/topics/db/models/) on models) if you are interested. There is also a root `urls.py` that determines the URL structure for the entire project. A starter `urls.py` is included in the default game template, and automatically imports all of Evennia's diff --git a/docs/source/Howtos/Web-Character-Generation.md b/docs/source/Howtos/Web-Character-Generation.md index 3d0fa7a381..d2582382e4 100644 --- a/docs/source/Howtos/Web-Character-Generation.md +++ b/docs/source/Howtos/Web-Character-Generation.md @@ -82,7 +82,7 @@ and *templates* (how the web page should be structured). Models are created in `mygame/web/chargen/models.py`. -A [Django database model](../Concepts/New-Models.md) is a Python class that describes the database storage of the +A [Django database model](../Concepts/Models.md) is a Python class that describes the database storage of the data you want to manage. Any data you choose to store is stored in the same database as the game and you have access to all the game's objects here.