diff --git a/docs/0.9.1/.buildinfo b/docs/0.9.1/.buildinfo index 5e92856f1b..7b21c98bc6 100644 --- a/docs/0.9.1/.buildinfo +++ b/docs/0.9.1/.buildinfo @@ -1,4 +1,4 @@ # Sphinx build info version 1 # This file hashes the configuration used when building these files. When it is not found, a full rebuild will be done. -config: 14d9ab78b5fce54058fbe5dcca8b58b4 +config: 363cb69f5409e46845ec6d04564df931 tags: 645f666f9bcd5a90fca523b33c5a78b7 diff --git a/docs/0.9.1/A-voice-operated-elevator-using-events.html b/docs/0.9.1/A-voice-operated-elevator-using-events.html index 5254bad70d..95f2b8a051 100644 --- a/docs/0.9.1/A-voice-operated-elevator-using-events.html +++ b/docs/0.9.1/A-voice-operated-elevator-using-events.html @@ -7,11 +7,13 @@ A voice operated elevator using events — Evennia 0.9.1 documentation + + @@ -25,7 +27,10 @@
  • modules |
  • - + + + + @@ -453,7 +458,10 @@
  • modules |
  • - + + + + @@ -45,7 +50,7 @@

    Griatch (Sept 2, 2019)

    -

    I don’t agree with removing explicit keywords as suggested by Johnny on Aug 29 below. Overriding such a method can still be done by get(self, **kwargs) if so desired, making the kwargs explicit helps IMO readability of the API. If just giving a generic **kwargs, one must read the docstring or even the code to see which keywords are valid.

    +

    I don’t agree with removing explicit keywords as suggested by Johnny on Aug 29 below. Overriding such a method can still be done by get(self, **kwargs) if so desired, making the kwargs explicit helps IMO readability of the API. If just giving a generic **kwargs, one must read the docstring or even the code to see which keywords are valid.

    On the other hand, I think it makes sense to as a standard offer an extra **kwargs at the end of arg-lists for common methods that are expected to be over-ridden. This make the API more flexible by hinting to the dev that they could expand their own over-ridden implementation with their own keyword arguments if so desired.


    @@ -122,7 +127,10 @@
  • modules |
  • - + + + + @@ -39,9 +44,9 @@

    All users (real people) that starts a game Session on Evennia are doing so through an object called Account. The Account object has no in-game representation, it represents a unique game account. In order to actually get on the game the Account must puppet an Object -(normally a Character).

    +(normally a Character).

    Exactly how many Sessions can interact with an Account and its Puppets at once is determined by -Evennia’s MULTISESSION_MODE setting.

    +Evennia’s MULTISESSION_MODE setting.

    Apart from storing login information and other account-specific data, the Account object is what is chatting on Channels. It is also a good place to store Permissions to be consistent between different in-game characters as well as configuration options. The Account @@ -52,7 +57,7 @@ like a simple chat program. It acts as a staging area for switching between Cha game supports that) or as a safety mode if your Character gets deleted. Use ic to attempt to (re)puppet a Character.

    Note that the Account object can have, and often does have, a different set of -Permissions from the Character they control. Normally you should put your +Permissions from the Character they control. Normally you should put your permissions on the Account level - this will overrule permissions set on the Character level. For the permissions of the Character to come into play the default quell command can be used. This allows for exploring the game using a different permission set (but you can’t escalate your @@ -126,7 +131,7 @@ a property sessid

    cmdset - This holds all the current Commands of this Account. By default these are the commands found in the cmdset defined by settings.CMDSET_ACCOUNT.

  • nicks - This stores and handles Nicks, in the same way as nicks it works on Objects. -For Accounts, nicks are primarily used to store custom aliases for Channels.

  • +For Accounts, nicks are primarily used to store custom aliases for Channels.

    Selection of special methods (see evennia.DefaultAccount for details):

    @@ -204,7 +209,10 @@ to.

  • modules |
  • - + + + + @@ -390,7 +395,10 @@ need something more custom, you will have to expand on the functions you use.

    modules | - + + + + @@ -47,7 +52,7 @@ it just mygameCmdEcho in this example.

  • Set the class variable key to a good command name, like echo.

  • Give your class a useful docstring. A docstring is the string at the very top of a class or function/method. The docstring at the top of the command class is read by Evennia to become the help entry for the Command (see -Command Auto-help).

  • +Command Auto-help).

  • Define a class method func(self) that echoes your input back to you.

  • Below is an example how this all could look for the echo command:

    @@ -146,7 +151,7 @@ the game. Use help< your command definition).

    Note: Typing echotest will also work. It will be handled as the command echo directly followed by -its argument test (which will end up in self.args). To change this behavior, you can add the arg_regexproperty alongsidekey, help_category` etc. See the arg_regex documentation for more info.

    +its argument test (which will end up in self.args). To change this behavior, you can add the arg_regexproperty alongsidekey, help_category` etc. See the arg_regex documentation for more info.

    If you want to overload existing default commands (such as look or get), just add your new command with the same key as the old one - it will then replace it. Just remember that you must use @@ -295,7 +300,10 @@ default character cmdset defaults to being defined as

  • modules |
  • - + + + + @@ -212,7 +217,10 @@ objects you can use modules | - + + + + @@ -45,11 +50,11 @@
  • Running Evennia in Docker Containers

  • Starting, stopping, reloading and resetting Evennia

  • Keeping your game up to date

  • -
  • Resetting your database

  • +
  • Resetting your database

  • Making your game available online

  • Listing your game at the online Evennia game index

  • @@ -58,7 +63,7 @@

    Customizing the server

    @@ -152,7 +157,10 @@
  • modules |
  • - + + + +
    @@ -273,7 +278,10 @@ port but this should be applicable also to other types of proxies (like nginx).<
  • modules |
  • - + + + + @@ -60,7 +65,7 @@ Instructions
    for your OS. The difference is that you need to If you are new to Evennia it’s highly recommended that you run through the instructions in full - including initializing and starting a new empty game and connecting to it. That way you can be sure Evennia works correctly as a base line. If you have trouble, make sure to -read the Troubleshooting instructions for your +read the Troubleshooting instructions for your operating system. You can also drop into our forums, join #evennia on irc.freenode.net or chat from the linked Discord Server.

    @@ -327,7 +332,10 @@ on localhost at port 4000, and the webserver at http://localhost:4001/

  • modules |
  • - + + + + @@ -347,7 +352,10 @@ your own liking.

  • modules |
  • - + + + + @@ -48,7 +53,7 @@ data in properties already defined on entities (such as key property no matter how hard you tried). Attributes come into play when you want to assign arbitrary data to arbitrary names.

    -

    Attributes are not secure by default and any player may be able to change them unless you prevent this behavior.

    +

    Attributes are not secure by default and any player may be able to change them unless you prevent this behavior.

    The .db and .ndb shortcuts

    To save persistent data on a Typeclassed object you normally use the db (DataBase) operator. Let’s @@ -118,7 +123,7 @@ supplied here to restrict future access and also the call itself may be checked before performing the deletion. - clear(...) - removes all Attributes from object.

  • all(...) - returns all Attributes (of the given category) attached to this object.

  • -

    See this section for more about locking down Attribute +

    See this section for more about locking down Attribute access and editing. The Nattribute offers no concept of access control.

    Some examples:

    1
    @@ -147,16 +152,16 @@ access and editing. The attrname.

  • value - this is the value of the Attribute. This value can be anything which can be pickled - objects, lists, numbers or what have you (see -this section for more info). In the example +this section for more info). In the example obj.db.attrname = value, the value is stored here.

  • category - this is an optional property that is set to None for most Attributes. Setting this allows to use Attributes for different functionality. This is usually not needed unless you want to use Attributes for very different functionality (Nicks is an example of using Attributes -in this way). To modify this property you need to use the Attribute Handler.

  • +in this way). To modify this property you need to use the Attribute Handler.

  • strvalue - this is a separate value field that only accepts strings. This severely limits the data possible to store, but allows for easier database lookups. This property is usually not used except when re-using Attributes for some other purpose (Nicks use it). It is only -accessible via the Attribute Handler.

  • +accessible via the Attribute Handler.

    There are also two special properties:

      @@ -216,7 +221,7 @@ not a big deal. But if you are accessing the Attribute as part of some big loop amount of reads/writes you should first extract it to a temporary variable, operate on that and then save the result back to the Attribute. If you are storing a more complex structure like a dict or a list you should make sure to “disconnect” it from the database before looping over it, -as mentioned in the Retrieving Mutable Objects section below.

      +as mentioned in the Retrieving Mutable Objects section below.

      Storing single objects

      With a single object, we mean anything that is not iterable, like numbers, strings or custom class instances without the __iter__ method.

      @@ -275,7 +280,7 @@ entities you can loop over in a for-loop. Attribute-saving supports the followin Since you can use any combination of the above iterables, this is generally not much of a limitation.

    -

    Any entity listed in the Single object section above can be stored in the iterable.

    +

    Any entity listed in the Single object section above can be stored in the iterable.

    As mentioned in the previous section, database entities (aka typeclasses) are not possible to pickle. So when storing an iterable, Evennia must recursively traverse the iterable and all its @@ -509,7 +514,10 @@ those will check for the modules | -

    + + + +
    @@ -208,7 +213,10 @@ have tried to be nice. Now you just want this troll gone.

  • modules |
  • - + + + +
    @@ -310,7 +315,10 @@
  • modules |
  • - + + + + @@ -246,7 +251,10 @@
  • modules |
  • - + + + + @@ -118,7 +123,10 @@
  • modules |
  • - + + + + @@ -165,7 +170,10 @@ Fixed-width containers take up a certain max-width of the page - they’re usefu
  • modules |
  • - + + + + @@ -176,7 +181,10 @@ to get a feel for how to add pages to Evennia’s website to test these examples
  • modules |
  • - + + + + @@ -45,9 +50,9 @@
  • Giving build permissions to others

  • Adding text tags

  • Customizing the connection screen

  • @@ -129,7 +134,10 @@
  • modules |
  • - + + + + @@ -127,7 +132,10 @@
  • modules |
  • - + + + + @@ -275,7 +280,10 @@ your command (so entering modules | - + + + + @@ -58,7 +63,7 @@ @@ -1501,7 +1506,10 @@ This is the northern exit. Cool huh?
  • modules |
  • - + + + + @@ -307,7 +312,10 @@ If you in the future wanted to completely wipe the database, an easy way to do i
  • modules |
  • - + + + + @@ -157,7 +162,10 @@
  • modules |
  • - + + + + @@ -40,17 +45,17 @@
    @@ -210,7 +215,7 @@ # ...
    -

    Add this to the default cmdset as usual. The is_full_moon lock function does not yet exist. We must create that:

    +

    Add this to the default cmdset as usual. The is_full_moon lock function does not yet exist. We must create that:

    1
     2
     3
    @@ -530,7 +535,10 @@ MuxCommand class is also found commented-out in the 
               modules |
    -         
    +        
    +
    +
    +
           
         
    @@ -151,7 +156,10 @@
  • modules |
  • - + + + + @@ -390,7 +395,10 @@ never raise a traceback but instead echo errors through logging. See
  • modules |
  • - + + + + @@ -42,7 +47,7 @@ command over and over. Or in an advanced combat system, a massive swing may offer a chance of lots of damage at the cost of not being able to re-do it for a while. Such effects are called cooldowns.

    This page exemplifies a very resource-efficient way to do cooldowns. A more -‘active’ way is to use asynchronous delays as in the command duration +‘active’ way is to use asynchronous delays as in the command duration tutorial, the two might be useful to combine if you want to echo some message to the user after the cooldown ends.

    @@ -227,7 +232,10 @@ other types of attacks for a while before the warrior can recover.

  • modules |
  • - + + + +
    @@ -89,7 +94,7 @@ this syntax. A short example will probably make it clear:

    Important: The yield functionality will only work in the func method of Commands. It only works because Evennia has especially catered for it in Commands. If you want the same functionality elsewhere you -must use the interactive decorator.

    +must use the interactive decorator.

    The important line is the yield 10. It tells Evennia to “pause” the command and to wait for 10 seconds to execute the rest. If you add this command and @@ -662,7 +667,10 @@ If you want to provide arguments for utils.delay to use, when calling your callb

  • modules |
  • - + + + + @@ -256,7 +261,10 @@
  • modules |
  • - + + + + @@ -38,13 +43,13 @@

    Command Sets

    Command Sets are intimately linked with Commands and you should be familiar with Commands before reading this page. The two pages were split for ease of reading.

    A Command Set (often referred to as a CmdSet or cmdset) is the basic unit for storing one or more Commands. A given Command can go into any number of different command sets. Storing Command classes in a command set is the way to make commands available to use in your game.

    -

    When storing a CmdSet on an object, you will make the commands in that command set available to the object. An example is the default command set stored on new Characters. This command set contains all the useful commands, from look and inventory to @dig and @reload (permissions then limit which players may use them, but that’s a separate topic).

    +

    When storing a CmdSet on an object, you will make the commands in that command set available to the object. An example is the default command set stored on new Characters. This command set contains all the useful commands, from look and inventory to @dig and @reload (permissions then limit which players may use them, but that’s a separate topic).

    When an account enters a command, cmdsets from the Account, Character, its location, and elsewhere are pulled together into a merge stack. This stack is merged together in a specific order to create a single “merged” cmdset, representing the pool of commands available at that very moment.

    An example would be a Window object that has a cmdset with two commands in it: look through window and open window. The command set would be visible to players in the room with the window, allowing them to use those commands only there. You could imagine all sorts of clever uses of this, like a Television object which had multiple commands for looking at it, switching channels and so on. The tutorial world included with Evennia showcases a dark room that replaces certain critical commands with its own versions because the Character cannot see.

    If you want a quick start into defining your first commands and using them with command sets, you can head over to the Adding Command Tutorial which steps through things without the explanations.

    Defining Command Sets

    -

    A CmdSet is, as most things in Evennia, defined as a Python class inheriting from the correct parent (evennia.CmdSet, which is a shortcut to evennia.commands.cmdset.CmdSet). The CmdSet class only needs to define one method, called at_cmdset_creation(). All other class parameters are optional, but are used for more advanced set manipulation and coding (see the merge rules section).

    +

    A CmdSet is, as most things in Evennia, defined as a Python class inheriting from the correct parent (evennia.CmdSet, which is a shortcut to evennia.commands.cmdset.CmdSet). The CmdSet class only needs to define one method, called at_cmdset_creation(). All other class parameters are optional, but are used for more advanced set manipulation and coding (see the merge rules section).

     1
      2
      3
    @@ -122,11 +127,11 @@
     

    Properties on Command Sets

    -

    There are several extra flags that you can set on CmdSets in order to modify how they work. All are optional and will be set to defaults otherwise. Since many of these relate to merging cmdsets, you might want to read the Adding and Merging Command Sets section for some of these to make sense.

    +

    There are several extra flags that you can set on CmdSets in order to modify how they work. All are optional and will be set to defaults otherwise. Since many of these relate to merging cmdsets, you might want to read the Adding and Merging Command Sets section for some of these to make sense.

    • key (string) - an identifier for the cmdset. This is optional, but should be unique. It is used for display in lists, but also to identify special merging behaviours using the key_mergetype dictionary below.

    • mergetype (string) - allows for one of the following string values: “Union”, “Intersect”, “Replace”, or “Remove”.

    • -
    • priority (int) - This defines the merge order of the merge stack - cmdsets will merge in rising order of priority with the highest priority set merging last. During a merger, the commands from the set with the higher priority will have precedence (just what happens depends on the merge type). If priority is identical, the order in the merge stack determines preference. The priority value must be greater or equal to -100. Most in-game sets should usually have priorities between 0 and 100. Evennia default sets have priorities as follows (these can be changed if you want a different distribution):

      +
    • priority (int) - This defines the merge order of the merge stack - cmdsets will merge in rising order of priority with the highest priority set merging last. During a merger, the commands from the set with the higher priority will have precedence (just what happens depends on the merge type). If priority is identical, the order in the merge stack determines preference. The priority value must be greater or equal to -100. Most in-game sets should usually have priorities between 0 and 100. Evennia default sets have priorities as follows (these can be changed if you want a different distribution):

      • EmptySet: -101 (should be lower than all other sets)

      • SessionCmdSet: -20

      • @@ -136,7 +141,7 @@
      • ChannelCmdSet: 101 (should usually always be available) - since exits never accept arguments, there is no collision between exits named the same as a channel even though the commands “collide”.

    • -
    • key_mergetype (dict) - a dict of key:mergetype pairs. This allows this cmdset to merge differently with certain named cmdsets. If the cmdset to merge with has a key matching an entry in key_mergetype, it will not be merged according to the setting in mergetype but according to the mode in this dict. Please note that this is more complex than it may seem due to the merge order of command sets. Please review that section before using key_mergetype.

    • +
    • key_mergetype (dict) - a dict of key:mergetype pairs. This allows this cmdset to merge differently with certain named cmdsets. If the cmdset to merge with has a key matching an entry in key_mergetype, it will not be merged according to the setting in mergetype but according to the mode in this dict. Please note that this is more complex than it may seem due to the merge order of command sets. Please review that section before using key_mergetype.

    • duplicates (bool/None default None) - this determines what happens when merging same-priority cmdsets containing same-key commands together. Thedupicate option will only apply when merging the cmdset with this option onto one other cmdset with the same priority. The resulting cmdset will not retain this duplicate setting.

      • None (default): No duplicates are allowed and the cmdset being merged “onto” the old one will take precedence. The result will be unique commands. However, the system will assume this value to be True for cmdsets on Objects, to avoid dangerous clashes. This is usually the safe bet.

      • @@ -167,7 +172,7 @@

    Command Sets Searched

    -

    When a user issues a command, it is matched against the merged command sets available to the player at the moment. Which those are may change at any time (such as when the player walks into the room with the Window object described earlier).

    +

    When a user issues a command, it is matched against the merged command sets available to the player at the moment. Which those are may change at any time (such as when the player walks into the room with the Window object described earlier).

    The currently valid command sets are collected from the following sources:

    @@ -39,7 +44,7 @@

    See also:

    @@ -136,7 +141,7 @@
  • cmdstring - the matched key for the command. This would be look in our example.

  • args - this is the rest of the string, except the command name. So if the string entered was look at sword, args would be “ at sword”. Note the space kept - Evennia would correctly interpret lookat sword too. This is useful for things like /switches that should not use space. In the MuxCommand class used for default commands, this space is stripped. Also see the arg_regex property if you want to enforce a space to make lookat sword give a command-not-found error.

  • obj - the game Object on which this command is defined. This need not be the caller, but since look is a common (default) command, this is probably defined directly on BigGuy - so obj will point to BigGuy. Otherwise obj could be an Account or any interactive object with commands defined on it, like in the example of the “check time” command defined on a “Clock” object.

  • -
  • cmdset - this is a reference to the merged CmdSet (see below) from which this command was matched. This variable is rarely used, it’s main use is for the auto-help system (Advanced note: the merged cmdset need NOT be the same as BigGuy.cmdset. The merged set can be a combination of the cmdsets from other objects in the room, for example).

  • +
  • cmdset - this is a reference to the merged CmdSet (see below) from which this command was matched. This variable is rarely used, it’s main use is for the auto-help system (Advanced note: the merged cmdset need NOT be the same as BigGuy.cmdset. The merged set can be a combination of the cmdsets from other objects in the room, for example).

  • raw_string - this is the raw input coming from the user, without stripping any surrounding whitespace. The only thing that is stripped is the ending newline marker.

  • @@ -162,8 +167,8 @@ display to the user. They are useful for creating listings and forms with colors
  • locks (string) - a lock definition, usually on the form cmd:<lockfuncs>. Locks is a rather big topic, so until you learn more about locks, stick to giving the lockstring "cmd:all()" to make the command available to everyone (if you don’t provide a lock string, this will be assigned for you).

  • help_category (optional string) - setting this helps to structure the auto-help into categories. If none is set, this will be set to General.

  • save_for_next (optional boolean). This defaults to False. If True, a copy of this command object (along with any changes you have done to it) will be stored by the system and can be accessed by the next command by retrieving self.caller.ndb.last_cmd. The next run command will either clear or replace the storage.

  • -
  • arg_regex (optional raw string): Used to force the parser to limit itself and tell it when the command-name ends and arguments begin (such as requiring this to be a space or a /switch). This is done with a regular expression. See the arg_regex section for the details.

  • -
  • auto_help (optional boolean). Defaults to True. This allows for turning off the auto-help system on a per-command basis. This could be useful if you either want to write your help entries manually or hide the existence of a command from help’s generated list.

  • +
  • arg_regex (optional raw string): Used to force the parser to limit itself and tell it when the command-name ends and arguments begin (such as requiring this to be a space or a /switch). This is done with a regular expression. See the arg_regex section for the details.

  • +
  • auto_help (optional boolean). Defaults to True. This allows for turning off the auto-help system on a per-command basis. This could be useful if you either want to write your help entries manually or hide the existence of a command from help’s generated list.

  • is_exit (bool) - this marks the command as being used for an in-game exit. This is, by default, set by all Exit objects and you should not need to set it manually unless you make your own Exit system. It is used for optimization and allows the cmdhandler to easily disregard this command when the cmdset has its no_exits flag set.

  • is_channel (bool)- this marks the command as being used for an in-game channel. This is, by default, set by all Channel objects and you should not need to set it manually unless you make your own Channel system. is used for optimization and allows the cmdhandler to easily disregard this command when its cmdset has its no_channels flag set.

  • msg_all_sessions (bool): This affects the behavior of the Command.msg method. If unset (default), calling self.msg(text) from the Command will always only send text to the Session that actually triggered this Command. If set however, self.msg(text) will send to all Sessions relevant to the object this Command sits on. Just which Sessions receives the text depends on the object and the server’s MULTISESSION_MODE.

  • @@ -453,7 +458,7 @@ everyone while waiting for that user to input their text. Inside a Command’s

    Dynamic Commands

    Note: This is an advanced topic.

    -

    Normally Commands are created as fixed classes and used without modification. There are however situations when the exact key, alias or other properties is not possible (or impractical) to pre-code (Exits is an example of this).

    +

    Normally Commands are created as fixed classes and used without modification. There are however situations when the exact key, alias or other properties is not possible (or impractical) to pre-code (Exits is an example of this).

    To create a command with a dynamic call signature, first define the command body normally in a class (set your key, aliases to default values), then use the following call (assuming the command class you created is named MyCommand):

    1
     2
    @@ -470,8 +475,8 @@ everyone while waiting for that user to input their text. Inside a Command’s
     

    Exits

    Note: This is an advanced topic.

    -

    Exits are examples of the use of a Dynamic Command.

    -

    The functionality of Exit objects in Evennia is not hard-coded in the engine. Instead Exits are normal typeclassed objects that auto-create a CmdSet on themselves when they load. This cmdset has a single dynamically created Command with the same properties (key, aliases and locks) as the Exit object itself. When entering the name of the exit, this dynamic exit-command is triggered and (after access checks) moves the Character to the exit’s destination. +

    Exits are examples of the use of a Dynamic Command.

    +

    The functionality of Exit objects in Evennia is not hard-coded in the engine. Instead Exits are normal typeclassed objects that auto-create a CmdSet on themselves when they load. This cmdset has a single dynamically created Command with the same properties (key, aliases and locks) as the Exit object itself. When entering the name of the exit, this dynamic exit-command is triggered and (after access checks) moves the Character to the exit’s destination. Whereas you could customize the Exit object and its command to achieve completely different behaviour, you will usually be fine just using the appropriate traverse_* hooks on the Exit object. But if you are interested in really changing how things work under the hood, check out evennia/objects/objects.py for how the Exit typeclass is set up.

    @@ -542,8 +547,8 @@ Whereas you could customize the Exit object and its command to achieve completel
  • The caller’s own currently active CmdSet.

  • CmdSets defined on the current account, if caller is a puppeted object.

  • CmdSets defined on the Session itself.

  • -
  • The active CmdSets of eventual objects in the same location (if any). This includes commands on Exits.

  • -
  • Sets of dynamically created System commands representing available Communications.

  • +
  • The active CmdSets of eventual objects in the same location (if any). This includes commands on Exits.

  • +
  • Sets of dynamically created System commands representing available Communications.

  • All CmdSets of the same priority are merged together in groups. Grouping avoids order-dependent issues of merging multiple same-prio sets onto lower ones.

  • @@ -659,7 +664,10 @@ thus do so asynchronously, using callbacks.

  • modules |
  • - + + + +
    @@ -165,7 +170,10 @@
  • modules |
  • - + + + +
    @@ -122,7 +127,10 @@ tutorial section on how to add new commands to a default command set.

  • modules |
  • - + + + + @@ -425,7 +430,10 @@ build steps could be added or removed at this point, adding some features like U
  • modules |
  • - + + + + @@ -69,7 +74,7 @@ chat/forum).

    bug or feature, just correcting typos, adjusting formatting or simply using the thing and reporting when stuff doesn’t make sense helps us a lot.

    The most elegant way to contribute code to Evennia is to use GitHub to create a fork of the -Evennia repository and make your changes to that. Refer to the Forking Evennia version +Evennia repository and make your changes to that. Refer to the Forking Evennia version control instructions for detailed instructions.

    Once you have a fork set up, you can not only work on your own game in a separate branch, you can also commit your fixes to Evennia itself. Make separate branches for all Evennia additions you do - @@ -167,7 +172,10 @@ Forked repository as described above.

  • modules |
  • - + + + + @@ -560,7 +565,10 @@ optimized to be quick and efficient.

  • modules |
  • - + + + + @@ -38,7 +43,7 @@

    Custom Protocols

    Note: This is considered an advanced topic and is mostly of interest to users planning to implement their own custom client protocol.

    -

    A PortalSession is the basic data object representing an external +

    A PortalSession is the basic data object representing an external connection to the Evennia Portal – usually a human player running a mud client of some kind. The way they connect (the language the player’s client and Evennia use to talk to each other) is called the connection Protocol. The most common such protocol for MUD:s is the @@ -413,7 +418,10 @@ ways.

  • modules |
  • - + + + + @@ -746,7 +751,10 @@ this article.

  • modules |
  • - + + + + @@ -363,7 +368,10 @@ command is not needed much in modules | - + + + + @@ -47,111 +52,111 @@ information about how commands work can be found in the documentation for A-Z
    • __unloggedin_look_command - look when in unlogged-in state

    • -
    • about - show Evennia info

    • -
    • access - show your current game access

    • -
    • addcom - add a channel alias and/or subscribe to a channel

    • -
    • alias - adding permanent aliases for object

    • -
    • allcom - perform admin operations on all channels

    • -
    • ban - ban an account from the server

    • -
    • batchcode - build from batch-code file

    • -
    • batchcommands - build from batch-command file

    • -
    • boot - kick an account from the server.

    • -
    • cboot - kick an account from a channel you control

    • -
    • ccreate - create a new channel

    • -
    • cdesc - describe a channel you control

    • -
    • cdestroy - destroy a channel you created

    • -
    • cemit - send an admin message to a channel you control

    • -
    • channels - list all channels available to you

    • -
    • charcreate - create a new character

    • -
    • chardelete - delete a character - this cannot be undone!

    • -
    • clock - change channel locks of a channel you control

    • -
    • cmdsets - list command sets defined on an object

    • -
    • color - testing which colors your client support

    • -
    • command - This is a parent class for some of the defining objmanip commands

    • -
    • connect - connect to the game

    • -
    • copy - copy an object and its properties

    • -
    • cpattr - copy attributes between objects

    • -
    • create - create a new account account

    • -
    • create - create new objects

    • -
    • cwho - show who is listening to a channel

    • -
    • delcom - remove a channel alias and/or unsubscribe from channel

    • -
    • desc - describe an object or the current room.

    • -
    • destroy - permanently delete objects

    • -
    • dig - build new rooms and connect them to the current location

    • -
    • drop - drop something

    • -
    • emit - admin command for emitting message to multiple objects

    • -
    • examine - get detailed information about an object

    • -
    • find - search the database for objects

    • -
    • force - forces an object to execute a command

    • -
    • get - pick up something

    • -
    • give - give away something to someone

    • -
    • help - get help when in unconnected-in state

    • -
    • help - View help or a list of topics

    • -
    • home - move to your character’s home location

    • -
    • ic - control an object you have permission to puppet

    • -
    • inventory - view inventory

    • -
    • irc2chan - Link an evennia channel to an external IRC channel

    • -
    • link - link existing rooms together with exits

    • -
    • lock - assign a lock definition to an object

    • -
    • look - look at location or object

    • -
    • look - look while out-of-character

    • -
    • mvattr - move attributes between objects

    • -
    • name - change the name and/or aliases of an object

    • -
    • nick - define a personal alias/nick by defining a string to

    • -
    • objects - statistics on objects in the database

    • -
    • ooc - stop puppeting and go ooc

    • -
    • open - open a new exit from the current room

    • -
    • option - Set an account option

    • -
    • page - send a private message to another account

    • -
    • password - change your password

    • -
    • perm - set the permissions of an account/object

    • -
    • pose - strike a pose

    • -
    • py - execute a snippet of python code

    • -
    • quell - use character’s permissions instead of account’s

    • -
    • quit - quit when in unlogged-in state

    • -
    • quit - quit the game

    • -
    • reload - reload the server

    • -
    • reset - reset and reboot the server

    • -
    • rss2chan - link an evennia channel to an external RSS feed

    • -
    • say - speak as your character

    • -
    • script - attach a script to an object

    • -
    • scripts - list and manage all running scripts

    • -
    • server - show server load and memory statistics

    • -
    • service - manage system services

    • -
    • sessions - check your connected session(s)

    • -
    • set - set attribute on an object or account

    • -
    • setdesc - describe yourself

    • -
    • sethelp - Edit the help database.

    • -
    • sethome - set an object’s home location

    • -
    • shutdown - stop the server completely

    • -
    • spawn - spawn objects from prototype

    • -
    • style - In-game style options

    • -
    • tag - handles the tags of an object

    • -
    • tel - teleport object to another location

    • -
    • time - show server time statistics

    • -
    • tunnel - create new rooms in cardinal directions only

    • -
    • typeclass - set or change an object’s typeclass

    • -
    • unban - remove a ban from an account

    • -
    • unlink - remove exit-connections between rooms

    • -
    • userpassword - change the password of an account

    • -
    • wall - make an announcement to all

    • -
    • whisper - Speak privately as your character to another

    • -
    • who - list who is currently online

    • -
    • wipe - clear all attributes from an object

    • +
    • about - show Evennia info

    • +
    • access - show your current game access

    • +
    • addcom - add a channel alias and/or subscribe to a channel

    • +
    • alias - adding permanent aliases for object

    • +
    • allcom - perform admin operations on all channels

    • +
    • ban - ban an account from the server

    • +
    • batchcode - build from batch-code file

    • +
    • batchcommands - build from batch-command file

    • +
    • boot - kick an account from the server.

    • +
    • cboot - kick an account from a channel you control

    • +
    • ccreate - create a new channel

    • +
    • cdesc - describe a channel you control

    • +
    • cdestroy - destroy a channel you created

    • +
    • cemit - send an admin message to a channel you control

    • +
    • channels - list all channels available to you

    • +
    • charcreate - create a new character

    • +
    • chardelete - delete a character - this cannot be undone!

    • +
    • clock - change channel locks of a channel you control

    • +
    • cmdsets - list command sets defined on an object

    • +
    • color - testing which colors your client support

    • +
    • command - This is a parent class for some of the defining objmanip commands

    • +
    • connect - connect to the game

    • +
    • copy - copy an object and its properties

    • +
    • cpattr - copy attributes between objects

    • +
    • create - create a new account account

    • +
    • create - create new objects

    • +
    • cwho - show who is listening to a channel

    • +
    • delcom - remove a channel alias and/or unsubscribe from channel

    • +
    • desc - describe an object or the current room.

    • +
    • destroy - permanently delete objects

    • +
    • dig - build new rooms and connect them to the current location

    • +
    • drop - drop something

    • +
    • emit - admin command for emitting message to multiple objects

    • +
    • examine - get detailed information about an object

    • +
    • find - search the database for objects

    • +
    • force - forces an object to execute a command

    • +
    • get - pick up something

    • +
    • give - give away something to someone

    • +
    • help - get help when in unconnected-in state

    • +
    • help - View help or a list of topics

    • +
    • home - move to your character’s home location

    • +
    • ic - control an object you have permission to puppet

    • +
    • inventory - view inventory

    • +
    • irc2chan - Link an evennia channel to an external IRC channel

    • +
    • link - link existing rooms together with exits

    • +
    • lock - assign a lock definition to an object

    • +
    • look - look at location or object

    • +
    • look - look while out-of-character

    • +
    • mvattr - move attributes between objects

    • +
    • name - change the name and/or aliases of an object

    • +
    • nick - define a personal alias/nick by defining a string to

    • +
    • objects - statistics on objects in the database

    • +
    • ooc - stop puppeting and go ooc

    • +
    • open - open a new exit from the current room

    • +
    • option - Set an account option

    • +
    • page - send a private message to another account

    • +
    • password - change your password

    • +
    • perm - set the permissions of an account/object

    • +
    • pose - strike a pose

    • +
    • py - execute a snippet of python code

    • +
    • quell - use character’s permissions instead of account’s

    • +
    • quit - quit when in unlogged-in state

    • +
    • quit - quit the game

    • +
    • reload - reload the server

    • +
    • reset - reset and reboot the server

    • +
    • rss2chan - link an evennia channel to an external RSS feed

    • +
    • say - speak as your character

    • +
    • script - attach a script to an object

    • +
    • scripts - list and manage all running scripts

    • +
    • server - show server load and memory statistics

    • +
    • service - manage system services

    • +
    • sessions - check your connected session(s)

    • +
    • set - set attribute on an object or account

    • +
    • setdesc - describe yourself

    • +
    • sethelp - Edit the help database.

    • +
    • sethome - set an object’s home location

    • +
    • shutdown - stop the server completely

    • +
    • spawn - spawn objects from prototype

    • +
    • style - In-game style options

    • +
    • tag - handles the tags of an object

    • +
    • tel - teleport object to another location

    • +
    • time - show server time statistics

    • +
    • tunnel - create new rooms in cardinal directions only

    • +
    • typeclass - set or change an object’s typeclass

    • +
    • unban - remove a ban from an account

    • +
    • unlink - remove exit-connections between rooms

    • +
    • userpassword - change the password of an account

    • +
    • wall - make an announcement to all

    • +
    • whisper - Speak privately as your character to another

    • +
    • who - list who is currently online

    • +
    • wipe - clear all attributes from an object

    @@ -2818,7 +2823,10 @@ Belongs to command set ‘DefaultUnloggedin’ of class modules | - + + + +
    @@ -223,7 +228,10 @@
  • modules |
  • - + + + + @@ -49,7 +54,7 @@
  • Getting started with Travis and Github for continuous integration testing

  • Planning your own Evennia game

  • First steps coding Evennia

  • -
  • Translating Evennia

  • +
  • Translating Evennia

  • Evennia Quirks to keep in mind.

  • Directions for configuring PyCharm with Evennia on Windows

  • @@ -92,7 +97,7 @@
  • Command System overview

  • Commands

  • Command Sets

  • -
  • Command Auto-help

  • +
  • Command Auto-help

  • @@ -238,7 +243,10 @@
  • modules |
  • - + + + + @@ -255,7 +260,10 @@
  • modules |
  • - + + + + @@ -148,7 +153,10 @@
  • modules |
  • - + + + + @@ -179,7 +184,10 @@ evennia/locks/lockhandler.py

  • modules |
  • - + + + + @@ -46,7 +51,7 @@

    There are at least two requirements needed for this tutorial to work.

    1. The structure of your mud has to follow a logical layout. Evennia supports the layout of your world to be ‘logically’ impossible with rooms looping to themselves or exits leading to the other side of the map. Exits can also be named anything, from “jumping out the window” to “into the fifth dimension”. This tutorial assumes you can only move in the cardinal directions (N, E, S and W).

    2. -
    3. Rooms must be connected and linked together for the map to be generated correctly. Vanilla Evennia comes with a admin command @tunnel that allows a user to create rooms in the cardinal directions, but additional work is needed to assure that rooms are connected. For example, if you @tunnel east and then immediately do @tunnel west you’ll find that you have created two completely stand-alone rooms. So care is needed if you want to create a “logical” layout. In this tutorial we assume you have such a grid of rooms that we can generate the map from.

    4. +
    5. Rooms must be connected and linked together for the map to be generated correctly. Vanilla Evennia comes with a admin command @tunnel that allows a user to create rooms in the cardinal directions, but additional work is needed to assure that rooms are connected. For example, if you @tunnel east and then immediately do @tunnel west you’ll find that you have created two completely stand-alone rooms. So care is needed if you want to create a “logical” layout. In this tutorial we assume you have such a grid of rooms that we can generate the map from.

    @@ -308,7 +313,10 @@
  • modules |
  • - + + + + @@ -522,14 +527,14 @@ needed. Here is an example:

    Examples:

    Example: Simple branching menu

    @@ -1136,7 +1141,7 @@ When asked 'Write something!', you answered 'Hello'. return True from the callback to repeat the prompt until you pass whatever check you want.

    Note: You cannot link consecutive questions by putting a new get_input call inside the -callback If you want that you should use an EvMenu instead (see the Repeating the same +callback If you want that you should use an EvMenu instead (see the Repeating the same node example above). Otherwise you can either peek at the implementation of get_input and implement your own mechanism (it’s just using cmdset nesting) or you can look at this extension suggested on the mailing @@ -1341,7 +1346,10 @@ until the exit node.

  • modules |
  • - + + + +
    @@ -124,7 +129,10 @@ paging.

  • modules |
  • - + + + +
    @@ -141,7 +146,10 @@ you then do

  • modules |
  • - + + + +
    @@ -175,7 +180,10 @@ if you are not ready for players yet.

  • modules |
  • - + + + + @@ -103,7 +108,7 @@

    I know basic Python, or I am willing to learn

    Evennia’s source code is extensively documented and is viewable online. We also have a comprehensive online manual with lots of examples. But while Python is considered a very easy programming language to get into, you do have a learning curve to climb if you are new to programming. You should probably sit down -with a Python beginner’s tutorial (there are plenty of them on the web if you look around) so you at least know what you are seeing. See also our link page for some reading suggestions. To efficiently code your dream game in Evennia you don’t need to be a Python guru, but you do need to be able to read example code containing at least these basic Python features:

    +with a Python beginner’s tutorial (there are plenty of them on the web if you look around) so you at least know what you are seeing. See also our link page for some reading suggestions. To efficiently code your dream game in Evennia you don’t need to be a Python guru, but you do need to be able to read example code containing at least these basic Python features:

    @@ -313,7 +318,10 @@ T 95
  • modules |
  • - + + + + @@ -49,7 +54,7 @@

    Collaborating on a game - Python vs Softcode

    -

    For a Player, collaborating on a game need not be too different between MUSH and Evennia. The building and description of the game world can still happen mostly in-game using build commands, using text tags and inline functions to prettify and customize the experience. Evennia offers external ways to build a world but those are optional. There is also nothing in principle stopping a Developer from offering a softcode-like language to Players if that is deemed necessary.

    +

    For a Player, collaborating on a game need not be too different between MUSH and Evennia. The building and description of the game world can still happen mostly in-game using build commands, using text tags and inline functions to prettify and customize the experience. Evennia offers external ways to build a world but those are optional. There is also nothing in principle stopping a Developer from offering a softcode-like language to Players if that is deemed necessary.

    For Developers of the game, the difference is larger: Code is mainly written outside the game in Python modules rather than in-game on the command line. Python is a very popular and well-supported language with tons of documentation and help to be found. The Python standard library is also a great help for not having to reinvent the wheel. But that said, while Python is considered one of the easier languages to learn and use it is undoubtedly very different from MUSH softcode.

    While softcode allows collaboration in-game, Evennia’s external coding instead opens up the possibility for collaboration using professional version control tools and bug tracking using websites like github (or bitbucket for a free private repo). Source code can be written in proper text editors and IDEs with refactoring, syntax highlighting and all other conveniences. In short, collaborative development of an Evennia game is done in the same way most professional collaborative development is done in the world, meaning all the best tools can be used.

    @@ -223,7 +228,10 @@ A big guy. His eyes are blue.
  • modules |
  • - + + + + @@ -61,7 +66,7 @@

    The permission hierarchy

    -

    Evennia has the following permission hierarchy 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 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.

    2. Helpers are like Players except they also have the ability to create/edit new help entries. This could be granted to players who are willing to help with writing lore or custom logs for everyone.

    3. @@ -70,7 +75,7 @@
    4. 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 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.

    @@ -930,7 +935,7 @@

    Channels

    -

    Evennia comes with Channels in-built and they are described fully in the documentation. For brevity, here are the relevant commands for normal use:

    +

    Evennia comes with Channels in-built and they are described fully in the documentation. For brevity, here are the relevant commands for normal use:

    @@ -50,7 +55,7 @@
  • obj - a dummy Object instance

  • evennia - Evennia’s flat API - through this you can access all of Evennia.

  • -

    For accessing other objects in the same room you need to use self.search(name). For objects in other locations, use one of the evennia.search_* methods. See below.

    +

    For accessing other objects in the same room you need to use self.search(name). For objects in other locations, use one of the evennia.search_* methods. See below.

    Returning output

    @@ -179,7 +184,10 @@ Out[3]: [<ObjectDB: Harry>, <ObjectDB: Limbo>, ...]
  • modules |
  • - + + + +
    @@ -359,7 +364,10 @@
  • modules |
  • - + + + + @@ -204,7 +209,10 @@
  • modules |
  • - + + + + @@ -404,7 +409,10 @@
  • modules |
  • - + + + + @@ -43,14 +48,14 @@ test out Evennia. Apart from downloading and updating you don’t even need an internet connection until you feel ready to share your game with the world.

    @@ -106,9 +111,9 @@ version is usually untested with Evennia)

    Linux Install

    If you run into any issues during the installation and first start, please -check out Linux Troubleshooting.

    +check out Linux Troubleshooting.

    For Debian-derived systems (like Ubuntu, Mint etc), start a terminal and -install the dependencies:

    +install the dependencies:

    sudo apt-get update
     sudo apt-get install python3 python3-pip python3-dev python3-setuptools python3-git python3-virtualenv gcc
     
    @@ -133,7 +138,7 @@ development:

    contains the source code though, it is not installed yet. To isolate the Evennia install and its dependencies from the rest of the system, it is good Python practice to install into a virtualenv. If you are unsure about what a -virtualenv is and why it’s useful, see the Glossary entry on +virtualenv is and why it’s useful, see the Glossary entry on virtualenv.

    Run python -V to see which version of Python your system defaults to.

    # If your Linux defaults to Python3.7+:
    @@ -165,8 +170,8 @@ folders) and run

    pip install -e evennia
     
    -

    For more info about pip, see the Glossary entry on pip. If -install failed with any issues, see Linux Troubleshooting.

    +

    For more info about pip, see the Glossary entry on pip. If +install failed with any issues, see Linux Troubleshooting.

    Next we’ll start our new game, here called “mygame”. This will create yet another new folder where you will be creating your new game:

    evennia --init mygame
    @@ -179,7 +184,7 @@ another new folder where you will be creating your new game:

    mygame/
    -

    You can configure Evennia extensively, for example +

    You can configure Evennia extensively, for example to use a different database. For now we’ll just stick to the defaults though.

    cd mygame
    @@ -193,14 +198,14 @@ live in the terminal, use 
     

    Your game should now be running! Open a web browser at http://localhost:4001 or point a telnet client to localhost:4000 and log in with the user you -created. Check out where to go next.

    +created. Check out where to go next.

    Mac Install

    The Evennia server is a terminal program. Open the terminal e.g. from Applications->Utilities->Terminal. Here is an introduction to the Mac terminal if you are unsure how it works. If you run into any issues during the -installation, please check out Mac Troubleshooting.

    +installation, please check out Mac Troubleshooting.

    • Python should already be installed but you must make sure it’s a high enough version. (This discusses @@ -226,7 +231,7 @@ install gcc and the Python headers.

    • contains the source code though, it is not installed yet. To isolate the Evennia install and its dependencies from the rest of the system, it is good Python practice to install into a virtualenv. If you are unsure about what a -virtualenv is and why it’s useful, see the Glossary entry on virtualenv.

      +virtualenv is and why it’s useful, see the Glossary entry on virtualenv.

      Run python -V to check which Python your system defaults to.

      # If your Mac defaults to Python3:
       virtualenv evenv
      @@ -256,8 +261,8 @@ folders) and run

      pip install -e evennia
      -

      For more info about pip, see the Glossary entry on pip. If -install failed with any issues, see Mac Troubleshooting.

      +

      For more info about pip, see the Glossary entry on pip. If +install failed with any issues, see Mac Troubleshooting.

      Next we’ll start our new game. We’ll call it “mygame” here. This creates a new folder where you will be creating your new game:

      evennia --init mygame
      @@ -270,7 +275,7 @@ folder where you will be creating your new game:

      mygame/
      -

      You can configure Evennia extensively, for example +

      You can configure Evennia extensively, for example to use a different database. We’ll go with the defaults here.

      cd mygame
      @@ -284,12 +289,12 @@ live in the terminal, use 
       

      Your game should now be running! Open a web browser at http://localhost:4001 or point a telnet client to localhost:4000 and log in with the user you -created. Check out where to go next.

      +created. Check out where to go next.

      Windows Install

      If you run into any issues during the installation, please check out -Windows Troubleshooting.

      +Windows Troubleshooting.

      If you are running Windows10, consider using the Windows Subsystem for Linux (WSL) instead. @@ -335,7 +340,7 @@ directory change.

      contains the source code though, it is not installed yet. To isolate the Evennia install and its dependencies from the rest of the system, it is good Python practice to install into a virtualenv. If you are unsure about what a -virtualenv is and why it’s useful, see the Glossary entry on virtualenv.

      +virtualenv is and why it’s useful, see the Glossary entry on virtualenv.

      In your console, try python -V to see which version of Python your system defaults to.

      pip install virtualenv
      @@ -373,8 +378,8 @@ folders when you use the 
      pip install -e evennia
       
      -

      For more info about pip, see the Glossary entry on pip. If -the install failed with any issues, see Windows Troubleshooting. +

      For more info about pip, see the Glossary entry on pip. If +the install failed with any issues, see Windows Troubleshooting. Next we’ll start our new game, we’ll call it “mygame” here. This creates a new folder where you will be creating your new game:

      evennia --init mygame
      @@ -387,7 +392,7 @@ creating your new game:

      mygame\
      -

      You can configure Evennia extensively, for example +

      You can configure Evennia extensively, for example to use a different database. We’ll go with the defaults here.

      cd mygame
      @@ -401,7 +406,7 @@ live in the terminal, use 

      Your game should now be running! Open a web browser at http://localhost:4001 or point a telnet client to localhost:4000 and log in with the user you -created. Check out where to go next.

      +created. Check out where to go next.

      Where to Go Next

      @@ -524,7 +529,10 @@ with pip modules | - + + + +
    @@ -38,83 +43,83 @@

    Glossary

    This explains common recurring terms used in the Evennia docs. It will be expanded as needed.

      -
    • account - the player’s account on the game

    • -
    • admin-site - the Django web page for manipulating the database

    • -
    • attribute - persistent, custom data stored on typeclasses

    • -
    • channel - game communication channels

    • -
    • character - the player’s avatar in the game, controlled from account

    • -
    • core - a term used for the code distributed with Evennia proper

    • -
    • django - web framework Evennia uses for database access and web integration

    • -
    • field - a typeclass property representing a database column

    • -
    • git - the version-control system we use

    • -
    • github - the online hosting of our source code

    • -
    • migrate - updating the database schema

    • +
    • account - the player’s account on the game

    • +
    • admin-site - the Django web page for manipulating the database

    • +
    • attribute - persistent, custom data stored on typeclasses

    • +
    • channel - game communication channels

    • +
    • character - the player’s avatar in the game, controlled from account

    • +
    • core - a term used for the code distributed with Evennia proper

    • +
    • django - web framework Evennia uses for database access and web integration

    • +
    • field - a typeclass property representing a database column

    • +
    • git - the version-control system we use

    • +
    • github - the online hosting of our source code

    • +
    • migrate - updating the database schema

    • multisession mode` - a setting defining how users connect to Evennia

    • -
    • object - Python instance, general term or in-game typeclass

    • -
    • pip - the Python installer

    • +
    • object - Python instance, general term or in-game typeclass

    • +
    • pip - the Python installer

    • player - the human connecting to the game with their client

    • -
    • puppet - when an account controls an in-game object

    • -
    • property - a python property

    • -
    • evenv - see virtualenv

    • -
    • repository - a store of source code + source history

    • -
    • script - a building block for custom storage, systems and time-keepint

    • -
    • session - represents one client connection

    • -
    • ticker - Allows to run events on a steady ‘tick’

    • -
    • twisted - networking engine responsible for Evennia’s event loop and communications

    • -
    • typeclass - Evennia’s database-connected Python class

    • -
    • upstream - see github

    • -
    • virtualenv - a Python program and way to make an isolated Python install

    • +
    • puppet - when an account controls an in-game object

    • +
    • property - a python property

    • +
    • evenv - see virtualenv

    • +
    • repository - a store of source code + source history

    • +
    • script - a building block for custom storage, systems and time-keepint

    • +
    • session - represents one client connection

    • +
    • ticker - Allows to run events on a steady ‘tick’

    • +
    • twisted - networking engine responsible for Evennia’s event loop and communications

    • +
    • typeclass - Evennia’s database-connected Python class

    • +
    • upstream - see github

    • +
    • virtualenv - a Python program and way to make an isolated Python install


    account

    -

    The term ‘account’ refers to the player’s unique account on the game. It is represented by the Account typeclass and holds things like email, password, configuration etc.

    -

    When a player connects to the game, they connect to their account. The account has no representation in the game world. Through their Account they can instead choose to puppet one (or more, depending on game mode) Characters in the game.

    -

    In the default multisession mode of Evennia, you immediately start puppeting a Character with the same name as your Account when you log in - mimicking how older servers used to work.

    +

    The term ‘account’ refers to the player’s unique account on the game. It is represented by the Account typeclass and holds things like email, password, configuration etc.

    +

    When a player connects to the game, they connect to their account. The account has no representation in the game world. Through their Account they can instead choose to puppet one (or more, depending on game mode) Characters in the game.

    +

    In the default multisession mode of Evennia, you immediately start puppeting a Character with the same name as your Account when you log in - mimicking how older servers used to work.

    admin-site

    -

    This usually refers to Django’s Admin site or database-administration web page (link to Django docs). The admin site is an automatically generated web interface to the database (it can be customized extensively). It’s reachable from the admin link on the default Evennia website you get with your server.

    +

    This usually refers to Django’s Admin site or database-administration web page (link to Django docs). The admin site is an automatically generated web interface to the database (it can be customized extensively). It’s reachable from the admin link on the default Evennia website you get with your server.

    attribute

    -

    The term Attribute should not be confused with (properties or fields. The Attribute represents arbitrary pieces of data that can be attached to any typeclassed entity in Evennia. Attributes allows storing new persistent data on typeclasses without changing their underlying database schemas. Read more about Attributes here.

    +

    The term Attribute should not be confused with (properties or fields. The Attribute represents arbitrary pieces of data that can be attached to any typeclassed entity in Evennia. Attributes allows storing new persistent data on typeclasses without changing their underlying database schemas. Read more about Attributes here.

    channel

    -

    A Channel refers to an in-game communication channel. It’s an entity that people subscribe to and which re-distributes messages between all subscribers. Such subscribers default to being Accounts, for out-of-game communication but could also be Objects (usually Characters) if one wanted to adopt Channels for things like in-game walkie-talkies or phone systems. It is represented by the Channel typeclass. You can read more about the comm system here.

    +

    A Channel refers to an in-game communication channel. It’s an entity that people subscribe to and which re-distributes messages between all subscribers. Such subscribers default to being Accounts, for out-of-game communication but could also be Objects (usually Characters) if one wanted to adopt Channels for things like in-game walkie-talkies or phone systems. It is represented by the Channel typeclass. You can read more about the comm system here.

    character

    -

    The Character is the term we use for the default avatar being puppeted by the account in the game world. It is represented by the Character typeclass (which is a child of Object). Many developers use children of this class to represent monsters and other NPCs. You can read more about it here.

    +

    The Character is the term we use for the default avatar being puppeted by the account in the game world. It is represented by the Character typeclass (which is a child of Object). Many developers use children of this class to represent monsters and other NPCs. You can read more about it here.

    django

    -

    Django is a professional and very popular Python web framework, similar to Rails for the Ruby language. It is one of Evennia’s central library dependencies (the other one is Twisted). Evennia uses Django for two main things - to map all database operations to Python and for structuring our web site.

    +

    Django is a professional and very popular Python web framework, similar to Rails for the Ruby language. It is one of Evennia’s central library dependencies (the other one is Twisted). Evennia uses Django for two main things - to map all database operations to Python and for structuring our web site.

    Through Django, we can work with any supported database (SQlite3, Postgres, MySQL …) using generic Python instead of database-specific SQL: A database table is represented in Django as a Python class (called a model). An Python instance of such a class represents a row in that table.

    -

    There is usually no need to know the details of Django’s database handling in order to use Evennia - it will handle most of the complexity for you under the hood using what we call typeclasses. But should you need the power of Django you can always get it. Most commonly people want to use “raw” Django when doing more advanced/custom database queries than offered by Evennia’s default search functions. One will then need to read about Django’s querysets. Querysets are Python method calls on a special form that lets you build complex queries. They get converted into optimized SQL queries under the hood, suitable for your current database. Here is our tutorial/explanation of Django queries.

    +

    There is usually no need to know the details of Django’s database handling in order to use Evennia - it will handle most of the complexity for you under the hood using what we call typeclasses. But should you need the power of Django you can always get it. Most commonly people want to use “raw” Django when doing more advanced/custom database queries than offered by Evennia’s default search functions. One will then need to read about Django’s querysets. Querysets are Python method calls on a special form that lets you build complex queries. They get converted into optimized SQL queries under the hood, suitable for your current database. Here is our tutorial/explanation of Django queries.

    By the way, Django (and Evennia) does allow you to fall through and send raw SQL if you really want to. It’s highly unlikely to be needed though; the Django database abstraction is very, very powerful.

    -

    The other aspect where Evennia uses Django is for web integration. On one end Django gives an infrastructure for wiring Python functions (called views) to URLs: the view/function is called when a user goes that URL in their browser, enters data into a form etc. The return is the web page to show. Django also offers templating with features such as being able to add special markers in HTML where it will insert the values of Python variables on the fly (like showing the current player count on the web page). Here is one of our tutorials on wiring up such a web page. Django also comes with the admin site, which automatically maps the database into a form accessible from a web browser.

    +

    The other aspect where Evennia uses Django is for web integration. On one end Django gives an infrastructure for wiring Python functions (called views) to URLs: the view/function is called when a user goes that URL in their browser, enters data into a form etc. The return is the web page to show. Django also offers templating with features such as being able to add special markers in HTML where it will insert the values of Python variables on the fly (like showing the current player count on the web page). Here is one of our tutorials on wiring up such a web page. Django also comes with the admin site, which automatically maps the database into a form accessible from a web browser.

    core

    -

    This term is sometimes used to represent the main Evennia library code suite, excluding its contrib directory. It can sometimes come up in code reviews, such as

    +

    This term is sometimes used to represent the main Evennia library code suite, excluding its contrib directory. It can sometimes come up in code reviews, such as

    Evennia is game-agnostic but this feature is for a particular game genre. So it does not belong in core. Better make it a contrib.

    field

    -

    A field or database field in Evennia refers to a property on a typeclass directly linked to an underlying database column. Only a few fixed properties per typeclass are database fields but they are often tied to the core functionality of that base typeclass (for example Objects store its location as a field). In all other cases, attributes are used to add new persistent data to the typeclass. Read more about typeclass properties here.

    +

    A field or database field in Evennia refers to a property on a typeclass directly linked to an underlying database column. Only a few fixed properties per typeclass are database fields but they are often tied to the core functionality of that base typeclass (for example Objects store its location as a field). In all other cases, attributes are used to add new persistent data to the typeclass. Read more about typeclass properties here.

    git

    Git is a version control tool. It allows us to track the development of the Evennia code by dividing it into units called commits. A ‘commit’ is sort of a save-spot - you save the current state of your code and can then come back to it later if later changes caused problems. By tracking commits we know what ‘version’ of the code we are currently using.

    -

    Evennia’s source code + its source history is jointly called a repository. This is centrally stored at our online home on GitHub. Everyone using or developing Evennia makes a ‘clone’ of this repository to their own computer - everyone automatically gets everything that is online, including all the code history.

    +

    Evennia’s source code + its source history is jointly called a repository. This is centrally stored at our online home on GitHub. Everyone using or developing Evennia makes a ‘clone’ of this repository to their own computer - everyone automatically gets everything that is online, including all the code history.

    -

    Don’t confuse Git and GitHub. The former is the version control system. The latter is a website (run by a company) that allows you to upload source code controlled by Git for others to see (among other things).

    +

    Don’t confuse Git and GitHub. The former is the version control system. The latter is a website (run by a company) that allows you to upload source code controlled by Git for others to see (among other things).

    Git allows multiple users from around the world to efficiently collaborate on Evennia’s code: People can make local commits on their cloned code. The commits they do can then be uploaded to GitHub and reviewed by the Evennia lead devs - and if the changes look ok they can be safely merged into the central Evennia code - and everyone can pull those changes to update their local copies.

    Developers using Evennia often uses Git on their own games in the same way - to track their changes and to help collaboration with team mates. This is done completely independently of Evennia’s Git usage.

    @@ -129,28 +134,28 @@

    migrate

    -

    This term is used for upgrading the database structure (it’s schema )to a new version. Most often this is due to Evennia’s upstream schema changing. When that happens you need to migrate that schema to the new version as well. Once you have used git to pull the latest changes, just cd into your game dir and run

    +

    This term is used for upgrading the database structure (it’s schema )to a new version. Most often this is due to Evennia’s upstream schema changing. When that happens you need to migrate that schema to the new version as well. Once you have used git to pull the latest changes, just cd into your game dir and run

    evennia migrate 
     
    -

    That should be it (see virtualenv if you get a warning that the evennia command is not available). See also Updating your game for more details.

    +

    That should be it (see virtualenv if you get a warning that the evennia command is not available). See also Updating your game for more details.

    Technically, migrations are shipped as little Python snippets of code that explains which database actions must be taken to upgrade from one version of the schema to the next. When you run the command above, those snippets are run in sequence.

    multisession mode

    -

    This term refers to the MULTISESSION_MODE setting, which has a value of 0 to 3. The mode alters how players can connect to the game, such as how many Sessions a player can start with one account and how many Characters they can control at the same time. It is described in detail here.

    +

    This term refers to the MULTISESSION_MODE setting, which has a value of 0 to 3. The mode alters how players can connect to the game, such as how many Sessions a player can start with one account and how many Characters they can control at the same time. It is described in detail here.

    github

    -

    Github is where Evennia’s source code and documentation is hosted. This online repository of code we also sometimes refer to as upstream.

    -

    GitHub is a business, offering free hosting to Open-source projects like Evennia. Despite the similarity in name, don’t confuse GitHub the website with Git, the versioning system. Github hosts Git repositories online and helps with collaboration and infrastructure. Git itself is a separate project.

    +

    Github is where Evennia’s source code and documentation is hosted. This online repository of code we also sometimes refer to as upstream.

    +

    GitHub is a business, offering free hosting to Open-source projects like Evennia. Despite the similarity in name, don’t confuse GitHub the website with Git, the versioning system. Github hosts Git repositories online and helps with collaboration and infrastructure. Git itself is a separate project.

    object

    -

    In general Python (and other object-oriented languages), an object is what we call the instance of a class. But one of Evennia’s core typeclasses is also called “Object”. To separate these in the docs we try to use object to refer to the general term and capitalized Object when we refer to the typeclass.

    -

    The Object is a typeclass that represents all in-game entities, including Characters, rooms, trees, weapons etc. Read more about Objects here.

    +

    In general Python (and other object-oriented languages), an object is what we call the instance of a class. But one of Evennia’s core typeclasses is also called “Object”. To separate these in the docs we try to use object to refer to the general term and capitalized Object when we refer to the typeclass.

    +

    The Object is a typeclass that represents all in-game entities, including Characters, rooms, trees, weapons etc. Read more about Objects here.

    pip

    @@ -164,32 +169,32 @@
  • pip install <folder> - install a Python package you have downloaded earlier (or cloned using git).

  • pip install -e <folder> - install a local package by just making a soft link to the folder. This means that if the code in <folder> changes, the installed Python package is immediately updated. If not using -e, one would need to run pip install --upgrade <folder> every time to make the changes available when you import this package into your code. Evennia is installed this way.

  • -

    For development, pip is usually used together with a virtualenv to install all packages and dependencies needed for a project in one, isolated location on the hard drive.

    +

    For development, pip is usually used together with a virtualenv to install all packages and dependencies needed for a project in one, isolated location on the hard drive.

    puppet

    -

    An account can take control and “play as” any Object. When doing so, we call this puppeting, (like puppeteering). Normally the entity being puppeted is of the Character subclass but it does not have to be.

    +

    An account can take control and “play as” any Object. When doing so, we call this puppeting, (like puppeteering). Normally the entity being puppeted is of the Character subclass but it does not have to be.

    property

    -

    A property is a general term used for properties on any Python object. The term also sometimes refers to the property built-in function of Python (read more here). Note the distinction between properties, fields and Attributes.

    +

    A property is a general term used for properties on any Python object. The term also sometimes refers to the property built-in function of Python (read more here). Note the distinction between properties, fields and Attributes.

    repository

    -

    A repository is a version control/git term. It represents a folder containing source code plus its versioning history.

    +

    A repository is a version control/git term. It represents a folder containing source code plus its versioning history.

    In Git’s case, that history is stored in a hidden folder .git. If you ever feel the need to look into this folder you probably already know enough Git to know why.

    -

    The evennia folder you download from us with git clone is a repository. The code on GitHub is often referred to as the ‘online repository’ (or the upstream repository). If you put your game dir under version control, that of course becomes a repository as well.

    +

    The evennia folder you download from us with git clone is a repository. The code on GitHub is often referred to as the ‘online repository’ (or the upstream repository). If you put your game dir under version control, that of course becomes a repository as well.

    script

    -

    When we refer to Scripts, we generally refer to the Script typeclass. Scripts are the mavericks of Evennia - they are like Objects but without any in-game existence. They are useful as custom places to store data but also as building blocks in persistent game systems. Since the can be initialized with timing capabilities they can also be used for long-time persistent time keeping (for fast updates other types of timers may be better though). Read more about Scripts here

    +

    When we refer to Scripts, we generally refer to the Script typeclass. Scripts are the mavericks of Evennia - they are like Objects but without any in-game existence. They are useful as custom places to store data but also as building blocks in persistent game systems. Since the can be initialized with timing capabilities they can also be used for long-time persistent time keeping (for fast updates other types of timers may be better though). Read more about Scripts here

    session

    -

    A Session is a Python object representing a single client connection to the server. A given human player could connect to the game from different clients and each would get a Session (even if you did not allow them to actually log in and get access to an account).

    -

    Sessions are not typeclassed and has no database persistence. But since they always exist (also when not logged in), they share some common functionality with typeclasses that can be useful for certain game states.

    +

    A Session is a Python object representing a single client connection to the server. A given human player could connect to the game from different clients and each would get a Session (even if you did not allow them to actually log in and get access to an account).

    +

    Sessions are not typeclassed and has no database persistence. But since they always exist (also when not logged in), they share some common functionality with typeclasses that can be useful for certain game states.

    ticker

    @@ -197,13 +202,13 @@

    typeclass

    -

    The typeclass is an Evennia-specific term. A typeclass allows developers to work with database-persistent objects as if they were normal Python objects. It makes use of specific Django features to link a Python class to a database table. Sometimes we refer to such code entities as being typeclassed.

    -

    Evennia’s main typeclasses are Account, Object, Script and Channel. Children of the base class (such as Character) will use the same database table as the parent, but can have vastly different Python capabilities (and persistent features through Attributes and Tags. A typeclass can be coded and treated pretty much like any other Python class except it must inherit (at any distance) from one of the base typeclasses. Also, creating a new instance of a typeclass will add a new row to the database table to which it is linked.

    -

    The core typeclasses in the Evennia library are all named DefaultAccount, DefaultObject etc. When you initialize your [game dir] you automatically get empty children of these, called Account, Object etc that you can start working with.

    +

    The typeclass is an Evennia-specific term. A typeclass allows developers to work with database-persistent objects as if they were normal Python objects. It makes use of specific Django features to link a Python class to a database table. Sometimes we refer to such code entities as being typeclassed.

    +

    Evennia’s main typeclasses are Account, Object, Script and Channel. Children of the base class (such as Character) will use the same database table as the parent, but can have vastly different Python capabilities (and persistent features through Attributes and Tags. A typeclass can be coded and treated pretty much like any other Python class except it must inherit (at any distance) from one of the base typeclasses. Also, creating a new instance of a typeclass will add a new row to the database table to which it is linked.

    +

    The core typeclasses in the Evennia library are all named DefaultAccount, DefaultObject etc. When you initialize your [game dir] you automatically get empty children of these, called Account, Object etc that you can start working with.

    twisted

    -

    Twisted is a heavy-duty asynchronous networking engine. It is one of Evennia’s two major library dependencies (the other one is Django). Twisted is what “runs” Evennia - it handles Evennia’s event loop. Twisted also has the building blocks we need to construct network protocols and communicate with the outside world; such as our MUD-custom version of Telnet, Telnet+SSL, SSH, webclient-websockets etc. Twisted also runs our integrated web server, serving the Django-based website for your game.

    +

    Twisted is a heavy-duty asynchronous networking engine. It is one of Evennia’s two major library dependencies (the other one is Django). Twisted is what “runs” Evennia - it handles Evennia’s event loop. Twisted also has the building blocks we need to construct network protocols and communicate with the outside world; such as our MUD-custom version of Telnet, Telnet+SSL, SSH, webclient-websockets etc. Twisted also runs our integrated web server, serving the Django-based website for your game.

    virtualenv

    @@ -216,7 +221,7 @@
  • <folder_name>\Scripts\activate (windows)

  • deactivate - turn off the currently activated virtualenv.

  • -

    A virtualenv is ‘activated’ only for the console/terminal it was started in, but it’s safe to activate the same virtualenv many times in different windows if you want. Once activated, all Python packages now installed with pip will install to evenv rather than to a global location like /usr/local/bin or C:\Program Files.

    +

    A virtualenv is ‘activated’ only for the console/terminal it was started in, but it’s safe to activate the same virtualenv many times in different windows if you want. Once activated, all Python packages now installed with pip will install to evenv rather than to a global location like /usr/local/bin or C:\Program Files.

    Note that if you have root/admin access you could install Evennia globally just fine, without using a virtualenv. It’s strongly discouraged and considered bad practice though. Experienced Python developers tend to rather create one new virtualenv per project they are working on, to keep the varying installs cleanly separated from one another.

    @@ -311,7 +316,10 @@
  • modules |
  • - + + + +
    @@ -152,7 +157,10 @@ it to your channel in-game.

  • modules |
  • - + + + +
    @@ -97,7 +102,10 @@
  • modules |
  • - + + + +
    @@ -146,7 +151,10 @@
  • modules |
  • - + + + +
    @@ -584,7 +589,10 @@
  • modules |
  • - + + + +
    @@ -98,7 +103,7 @@

    Database help entries

    -

    These are all help entries not involving commands (this is handled automatically by the Command Auto-help system). Non-automatic help entries describe how your particular game is played - its rules, world descriptions and so on.

    +

    These are all help entries not involving commands (this is handled automatically by the Command Auto-help system). Non-automatic help entries describe how your particular game is played - its rules, world descriptions and so on.

    A help entry consists of four parts:

    @@ -115,7 +120,10 @@
  • modules |
  • - + + + +
    @@ -217,7 +222,10 @@
  • modules |
  • - + + + +
    @@ -141,7 +146,10 @@
  • modules |
  • - + + + + @@ -411,7 +416,10 @@ your rules modules | - + + + + @@ -36,7 +41,7 @@

    Inputfuncs

    -

    An inputfunc is an Evennia function that handles a particular input (an inputcommand) from the client. The inputfunc is the last destination for the inputcommand along the ingoing message path. The inputcommand always has the form (commandname, (args), {kwargs}) and Evennia will use this to try to find and call an inputfunc on the form

    +

    An inputfunc is an Evennia function that handles a particular input (an inputcommand) from the client. The inputfunc is the last destination for the inputcommand along the ingoing message path. The inputcommand always has the form (commandname, (args), {kwargs}) and Evennia will use this to try to find and call an inputfunc on the form

    1
     2
        def commandname(session, *args, **kwargs):
             # ...
    @@ -252,7 +257,10 @@ Output: ("get_
             
  • modules |
  • - + + + +
    @@ -88,7 +93,7 @@ Next, let’s activate our new virtualenv. Every time you want to work on Evenni
    (evenv) $ pip install --upgrade -e 'git+https://github.com/evennia/evennia#egg=evennia' 
     
    -

    This step will possibly take quite a while - we are downloading Evennia and are then installing it, building all of the requirements for Evennia to run. If you run into trouble on this step, please see Troubleshooting.

    +

    This step will possibly take quite a while - we are downloading Evennia and are then installing it, building all of the requirements for Evennia to run. If you run into trouble on this step, please see Troubleshooting.

    You can go to the dir where Evennia is installed with cd $VIRTUAL_ENV/src/evennia. git grep (something) can be handy, as can git diff

    @@ -117,7 +122,7 @@ mygame evenv (evenv) $ evennia start
    -

    You may wish to look at the Linux Instructions for more.

    +

    You may wish to look at the Linux Instructions for more.

    Caveats

    @@ -203,7 +208,10 @@ mygame evenv
  • modules |
  • - + + + +
    @@ -145,7 +150,10 @@ again:

  • modules |
  • - + + + + @@ -118,7 +123,10 @@ This engine is built using a single interactive object type, the Room class. The
  • modules |
  • - + + + + @@ -103,7 +108,10 @@ as Evennia itself, unless the individual contributor has specifically defined ot
  • modules |
  • - + + + + @@ -228,7 +233,10 @@
  • modules |
  • - + + + + @@ -36,7 +41,7 @@

    Locks

    -

    For most games it is a good idea to restrict what people can do. In Evennia such restrictions are applied and checked by something called locks. All Evennia entities (Commands, Objects, Scripts, Accounts, Help System, messages and channels) are accessed through locks.

    +

    For most games it is a good idea to restrict what people can do. In Evennia such restrictions are applied and checked by something called locks. All Evennia entities (Commands, Objects, Scripts, Accounts, Help System, messages and channels) are accessed through locks.

    A lock can be thought of as an “access rule” restricting a particular use of an Evennia entity. Whenever another entity wants that kind of access the lock will analyze that entity in different ways to determine if access should be granted or not. Evennia implements a “lockdown” philosophy - all entities are inaccessible unless you explicitly define a lock that allows some or full access.

    Let’s take an example: An object has a lock on itself that restricts how people may “delete” that object. Apart from knowing that it restricts deletion, the lock also knows that only players with the specific ID of, say, 34 are allowed to delete it. So whenever a player tries to run delete on the object, the delete command makes sure to check if this player is really allowed to do so. It calls the lock, which in turn checks if the player’s id is 34. Only then will it allow delete to go on with its job.

    @@ -106,12 +111,12 @@
  • attrcreate - who may create new attributes on the object (default True)

  • -
  • Characters:

    +
  • Characters:

    • Same as for Objects

  • -
  • Exits:

    +
  • Exits:

    • Same as for Objects

    • traverse - who may pass the exit.

    • @@ -132,7 +137,7 @@
    • attredit - change/delete attribute

  • -
  • Channels:

    +
  • Channels:

    • control - who is administrating the channel. This means the ability to delete the channel, boot listeners etc.

    • send - who may send to the channel.

    • @@ -209,7 +214,7 @@
      • true()/all() - give access to everyone

      • false()/none()/superuser() - give access to none. Superusers bypass the check entirely and are thus the only ones who will pass this check.

      • -
      • perm(perm) - this tries to match a given permission property, on an Account firsthand, on a Character second. See below.

      • +
      • perm(perm) - this tries to match a given permission property, on an Account firsthand, on a Character second. See below.

      • perm_above(perm) - like perm but requires a “higher” permission level than the one given.

      • id(num)/dbref(num) - checks so the access_object has a certain dbref/id.

      • attr(attrname) - checks if a certain Attribute exists on accessing_object.

      • @@ -294,7 +299,7 @@
  • Note the use of the /account switch. It means you assign the permission to the Accounts Tommy instead of any Character that also happens to be named “Tommy”.

    -

    Putting permissions on the Account 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 - as mentioned above, 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).

    +

    Putting permissions on the Account 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 - as mentioned above, 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).

    Below is an example of an object without any connected account

    1
     2
    @@ -496,7 +501,10 @@
             
  • modules |
  • - + + + +
    @@ -37,7 +42,7 @@

    Manually Configuring Color

    This is a small tutorial for customizing your character objects, using the example of letting users turn on and off ANSI color parsing as an example. @options NOCOLOR=True will now do what this tutorial shows, but the tutorial subject can be applied to other toggles you may want, as well.

    -

    In the Building guide’s Colors page you can learn how to add color to your game by using special markup. Colors enhance the gaming experience, but not all users want color. Examples would be users working from clients that don’t support color, or people with various seeing disabilities that rely on screen readers to play your game. Also, whereas Evennia normally automatically detects if a client supports color, it may get it wrong. Being able to turn it on manually if you know it should work could be a nice feature.

    +

    In the Building guide’s Colors page you can learn how to add color to your game by using special markup. Colors enhance the gaming experience, but not all users want color. Examples would be users working from clients that don’t support color, or people with various seeing disabilities that rely on screen readers to play your game. Also, whereas Evennia normally automatically detects if a client supports color, it may get it wrong. Being able to turn it on manually if you know it should work could be a nice feature.

    So here’s how to allow those users to remove color. It basically means you implementing a simple configuration system for your characters. This is the basic sequence:

    1. Define your own default character typeclass, inheriting from Evennia’s default.

    2. @@ -206,7 +211,7 @@

    More colors

    -

    Apart from ANSI colors, Evennia also supports Xterm256 colors (See Colors). The msg() method supports the xterm256 keyword for manually activating/deactiving xterm256. It should be easy to expand the above example to allow players to customize xterm256 regardless of if Evennia thinks their client supports it or not.

    +

    Apart from ANSI colors, Evennia also supports Xterm256 colors (See Colors). The msg() method supports the xterm256 keyword for manually activating/deactiving xterm256. It should be easy to expand the above example to allow players to customize xterm256 regardless of if Evennia thinks their client supports it or not.

    To get a better understanding of how msg() works with keywords, you can try this as superuser:

    @py self.msg("|123Dark blue with xterm256, bright blue with ANSI", xterm256=True)
     @py self.msg("|gThis should be uncolored", nomarkup=True)
    @@ -271,7 +276,10 @@
             
  • modules |
  • - + + + +
    @@ -215,7 +220,10 @@ the following message in the elevator’s appearance: modules | - + + + +
    @@ -158,7 +163,7 @@ active, the string will be encrypted.

    This will intelligently convert different input to the same form. So msg("Hello") will end up as an outputcommand ("text", ("Hello",), {}).

    -

    This is also the point where Inlinefuncs are parsed, depending on the session to receive the data. Said data is pickled together with the Session id then sent over the AMP bridge.

    +

    This is also the point where Inlinefuncs are parsed, depending on the session to receive the data. Said data is pickled together with the Session id then sent over the AMP bridge.

    PortalSessionHandler (outgoing)

    @@ -247,7 +252,10 @@ active, the string will be encrypted.

  • modules |
  • - + + + +
    @@ -165,7 +170,10 @@
  • modules |
  • - + + + + @@ -422,7 +427,7 @@ _________________________________________________________ self.caller.msg("The shop %s was created!" % shop)
    -

    Our typeclass is simple and so is our buildshop command. The command (which is for Builders only) just takes the name of the shop and builds the front room and a store room to go with it (always named "<shopname>-storage". It connects the rooms with a two-way exit. You need to add CmdBuildShop to the default cmdset before you can use it. Once having created the shop you can now @teleport to it or @open a new exit to it. You could also easily expand the above command to automatically create exits to and from the new shop from your current location.

    +

    Our typeclass is simple and so is our buildshop command. The command (which is for Builders only) just takes the name of the shop and builds the front room and a store room to go with it (always named "<shopname>-storage". It connects the rooms with a two-way exit. You need to add CmdBuildShop to the default cmdset before you can use it. Once having created the shop you can now @teleport to it or @open a new exit to it. You could also easily expand the above command to automatically create exits to and from the new shop from your current location.

    To avoid customers walking in and stealing everything, we create a Lock on the storage door. It’s a simple lock that requires the one entering to carry an object named <shopname>-storekey. We even create such a key object and drop it in the shop for the new shop keeper to pick up.

    If players are given the right to name their own objects, this simple lock is not very secure and you need to come up with a more robust lock-key solution.

    @@ -504,7 +509,10 @@ _________________________________________________________
  • modules |
  • - + + + +
    @@ -294,7 +299,10 @@ interested in for now is modules | - + + + + @@ -197,7 +202,10 @@
  • modules |
  • - + + + + @@ -213,7 +218,10 @@
  • modules |
  • - + + + + @@ -87,7 +92,7 @@
  • aliases - a handler that allows you to add and remove aliases from this object. Use aliases.add() to add a new alias and aliases.remove() to remove one.

  • location - a reference to the object currently containing this object.

  • home is a backup location. The main motivation is to have a safe place to move the object to if its location is destroyed. All objects should usually have a home location for safety.

  • -
  • destination - this holds a reference to another object this object links to in some way. Its main use is for Exits, it’s otherwise usually unset.

  • +
  • destination - this holds a reference to another object this object links to in some way. Its main use is for Exits, it’s otherwise usually unset.

  • nicks - as opposed to aliases, a Nick holds a convenient nickname replacement for a real name, word or sequence, only valid for this object. This mainly makes sense if the Object is used as a game character - it can then store briefer shorts, example so as to quickly reference game commands or other characters. Use nicks.add(alias, realname) to add a new one.

  • account - this holds a reference to a connected Account controlling this object (if any). Note that this is set also if the controlling account is not currently online - to test if an account is online, use the has_account property instead.

  • sessions - if account field is set and the account is online, this is a list of all active sessions (server connections) to contact them through (it may be more than one if multiple connections are allowed in settings).

  • @@ -97,7 +102,7 @@

    The last two properties are special:

      -
    • cmdset - this is a handler that stores all command sets defined on the object (if any).

    • +
    • cmdset - this is a handler that stores all command sets defined on the object (if any).

    • scripts - this is a handler that manages Scripts attached to the object (if any).

    The Object also has a host of useful utility functions. See the function headers in src/objects/objects.py for their arguments and more details.

    @@ -107,7 +112,7 @@
  • search() - this is a convenient shorthand to search for a specific object, at a given location or globally. It’s mainly useful when defining commands (in which case the object executing the command is named caller and one can do caller.search() to find objects in the room to operate on).

  • execute_cmd() - Lets the object execute the given string as if it was given on the command line.

  • move_to - perform a full move of this object to a new location. This is the main move method and will call all relevant hooks, do all checks etc.

  • -
  • clear_exits() - will delete all Exits to and from this object.

  • +
  • clear_exits() - will delete all Exits to and from this object.

  • clear_contents() - this will not delete anything, but rather move all contents (except Exits) to their designated Home locations.

  • delete() - deletes this object, first calling clear_exits() and clear_contents().

  • @@ -122,7 +127,7 @@

    Characters are objects controlled by Accounts. When a new Account logs in to Evennia for the first time, a new Character object is created and the Account object is assigned to the account attribute. A Character object -must have a Default Commandset set on itself at +must have a Default Commandset set on itself at creation, or the account will not be able to issue any commands! If you just inherit your own class from evennia.DefaultCharacter and make sure to use super() to call the parent methods you should be fine. In @@ -221,7 +226,10 @@ to modify.

  • modules |
  • - + + + + @@ -66,7 +71,7 @@

    Settings example

    -

    You can connect Evennia to the Internet without any changes to your settings. The default settings are easy to use but are not necessarily the safest. You can customize your online presence in your settings file. To have Evennia recognize changed port settings you have to do a full evennia reboot to also restart the Portal and not just the Server component.

    +

    You can connect Evennia to the Internet without any changes to your settings. The default settings are easy to use but are not necessarily the safest. You can customize your online presence in your settings file. To have Evennia recognize changed port settings you have to do a full evennia reboot to also restart the Portal and not just the Server component.

    Below is an example of a simple set of settings, mostly using the defaults. Evennia will require access to five computer ports, of which three (only) should be open to the outside world. Below we continue to assume that our server address is 203.0.113.0.

     1
      2
    @@ -286,7 +291,7 @@ The internal port (
     

    Setting up your own machine as a server

    -

    The first section of this page describes how to do this and allow users to connect to the IP address of your machine/router.

    +

    The first section of this page describes how to do this and allow users to connect to the IP address of your machine/router.

    A complication with using a specific IP address like this is that your home IP might not remain the same. Many ISPs (Internet Service Providers) allocates a dynamic IP to you which could change at any time. When that happens, that IP you told people to go to will be worthless. Also, that long string of numbers is not very pretty, is it? It’s hard to remember and not easy to use in marketing your game. What you need is to alias it to a more sensible domain name - an alias that follows you around also when the IP changes.

    1. To set up a domain name alias, we recommend starting with a free domain name from FreeDNS. Once you register there (it’s free) you have access to tens of thousands domain names that people have “donated” to allow you to use for your own sub domain. For example, strangled.net is one of those available domains. So tying our IP address to strangled.net using the subdomain evennia would mean that one could henceforth direct people to http://evennia.strangled.net:4001 for their gaming needs - far easier to remember!

    2. @@ -429,7 +434,10 @@ The internal port ( modules | - + + + +
    @@ -944,7 +949,10 @@ You played 1, you have won!
  • modules |
  • - + + + +
    @@ -91,7 +96,10 @@ This allows the two programs to communicate seamlessly.

  • modules |
  • - + + + + @@ -171,7 +176,10 @@ course hard to actually mimic human user behavior. For this, actual real-game te
  • modules |
  • - + + + + @@ -181,7 +186,10 @@ it still works correctly with Twisted on Python 3.

  • modules |
  • - + + + + @@ -39,11 +44,11 @@

    This is the first part of our beginner’s guide to the basics of using Python with Evennia. It’s aimed at you with limited or no programming/Python experience. But also if you are an experienced programmer new to Evennia or Python you might still pick up a thing or two. It is by necessity brief and low on detail. There are countless Python guides and tutorials, books and videos out there for learning more in-depth - use them!

    Contents:

    This quickstart assumes you have gotten Evennia started. You should make sure that you are able to see the output from the server in the console from which you started it. Log into the game either with a mud client on localhost:4000 or by pointing a web browser to localhost:4001/webclient. Log in as your superuser (the user you created during install).

    @@ -245,7 +250,10 @@ Hello world!
  • modules |
  • - + + + + @@ -39,11 +44,11 @@

    In the first part of this Python-for-Evennia basic tutorial we learned how to run some simple Python code from inside the game. We also made our first new module containing a function that we called. Now we’re going to start exploring the very important subject of objects.

    Contents:

    On the subject of objects

    @@ -144,7 +149,7 @@ object.

    ...
    -

    There are lots of things in there. There are some docs but most of those have to do with the distribution of Evennia and does not concern us right now. The evennia subfolder is what we are looking for. This is what you are accessing when you do from evennia import .... It’s set up by Evennia as a good place to find modules when the server starts. The exact layout of the Evennia library is covered by our directory overview. You can also explore it online on github.

    +

    There are lots of things in there. There are some docs but most of those have to do with the distribution of Evennia and does not concern us right now. The evennia subfolder is what we are looking for. This is what you are accessing when you do from evennia import .... It’s set up by Evennia as a good place to find modules when the server starts. The exact layout of the Evennia library is covered by our directory overview. You can also explore it online on github.

    The structure of the library directly reflects how you import from it.

    @@ -159,7 +164,10 @@
  • modules |
  • - + + + + @@ -124,7 +129,10 @@
  • modules |
  • - + + + + @@ -84,7 +89,10 @@
  • modules |
  • - + + + + @@ -286,7 +291,10 @@ f6d4ca9b2b22 mygame "/bin/sh -c 'evenn...&quo
  • modules |
  • - + + + + @@ -89,7 +94,10 @@
  • modules |
  • - + + + + @@ -464,7 +469,10 @@ tutorial.

  • modules |
  • - + + + + @@ -192,7 +197,10 @@ server from these locations if you like to work remotely or don’t have a home
  • modules |
  • - + + + + @@ -71,7 +76,7 @@
  • at_initial_setup.py - this allows you to add a custom startup method to be called (only) the very first time Evennia starts (at the same time as user #1 and Limbo is created). It can be made to start your own global scripts or set up other system/world-related things your game needs to have running from the start.

  • at_server_startstop.py - this module contains two functions that Evennia will call every time the Server starts and stops respectively - this includes stopping due to reloading and resetting as well as shutting down completely. It’s a useful place to put custom startup code for handlers and other things that must run in your game but which has no database persistence.

  • connection_screens.py - all global string variables in this module are interpreted by Evennia as a greeting screen to show when an Account first connects. If more than one string variable is present in the module a random one will be picked.

  • -
  • inlinefuncs.py - this is where you can define custom Inline functions.

  • +
  • inlinefuncs.py - this is where you can define custom Inline functions.

  • inputfuncs.py - this is where you define custom Input functions to handle data from the client.

  • lockfuncs.py - this is one of many possible modules to hold your own “safe” lock functions to make available to Evennia’s Locks.

  • mssp.py - this holds meta information about your game. It is used by MUD search engines (which you often have to register with) in order to display what kind of game you are running along with @@ -148,7 +153,10 @@ know about if you are an Evennia developer.

  • modules |
  • - + + + + @@ -188,7 +193,10 @@ on building new protocols.

  • modules |
  • - + + + + @@ -178,7 +183,10 @@ and you can click through the tabs to check appropriate logs, or even the consol
  • modules |
  • - + + + + @@ -198,7 +203,10 @@ decorator (only relevant for unit testing)

  • modules |
  • - + + + + @@ -176,7 +181,10 @@ pseudo-softcode plugin aimed at developers wanting to script their game from ins
  • modules |
  • - + + + + @@ -93,7 +98,7 @@ more lenient and will use defaults for keys not explicitly provided.

    Note that the prototype dict as given on the command line must be a valid Python structure - so you need to put quotes around strings etc. For security reasons, a dict inserted from-in game cannot have any other advanced Python functionality, such as executable code, lambda etc. If builders are supposed -to be able to use such features, you need to offer them through $protfuncs, embedded runnable functions that you have full control to check and vet before running.

    +to be able to use such features, you need to offer them through $protfuncs, embedded runnable functions that you have full control to check and vet before running.

    Prototype keys

    @@ -135,7 +140,7 @@ convenient for simple Attributes - use ndb_<name> - sets the value of a non-persistent attribute ("ndb_" is stripped from the name). This is simply not useful in a prototype and is deprecated.

  • exec - This accepts a code snippet or a list of code snippets to run. This should not be used - -use callables or $protfuncs instead (see below).

  • +use callables or $protfuncs instead (see below).

    @@ -166,7 +171,7 @@ prototype:

    At execution time, the place of the protfunc will be replaced with the result of that protfunc being called (this is always a string). A protfunc works in much the same way as an -InlineFunc - they are actually +InlineFunc - they are actually parsed using the same parser - except protfuncs are run every time the prototype is used to spawn a new object (whereas an inlinefunc is called when a text is returned to the user).

    Here is how a protfunc is defined (same as an inlinefunc).

    1
    @@ -372,7 +377,10 @@ in settings.PROTOTY
             
  • modules |
  • - + + + +
    @@ -40,7 +45,7 @@ program. If the evennia program is not available on the command line you must first install Evennia as described in the Getting Started page.

    -

    Hint: If you ever try the evennia command and get an error complaining that the command is not available, make sure your virtualenv is active.

    +

    Hint: If you ever try the evennia command and get an error complaining that the command is not available, make sure your virtualenv is active.

    Below are described the various management options. Run

    evennia -h
    @@ -282,7 +287,10 @@ In-game you should now get the message that the Server has successfully restarte
             
  • modules |
  • - + + + +
    @@ -59,7 +64,7 @@ Exits: north(#8), east(#9), south(#10), west(#11)
    -

    We will henceforth assume your game folder is name named mygame and that you haven’t modified the default commands. We will also not be using Colors for our map since they don’t show in the documentation wiki.

    +

    We will henceforth assume your game folder is name named mygame and that you haven’t modified the default commands. We will also not be using Colors for our map since they don’t show in the documentation wiki.

    Planning the Map

    @@ -83,7 +88,7 @@ Exits: north(#8), east(#9), south(#10), west(#11)

    Creating a Map Object

    In this section we will try to create an actual “map” object that an account can pick up and look at.

    -

    Evennia offers a range of default commands for creating objects and rooms in-game. While readily accessible, these commands are made to do very specific, restricted things and will thus not offer as much flexibility to experiment (for an advanced exception see in-line functions). Additionally, entering long descriptions and properties over and over in the game client can become tedious; especially when testing and you may want to delete and recreate things over and over.

    +

    Evennia offers a range of default commands for creating objects and rooms in-game. While readily accessible, these commands are made to do very specific, restricted things and will thus not offer as much flexibility to experiment (for an advanced exception see in-line functions). Additionally, entering long descriptions and properties over and over in the game client can become tedious; especially when testing and you may want to delete and recreate things over and over.

    To overcome this, Evennia offers batch processors that work as input-files created out-of-game. In this tutorial we’ll be using the more powerful of the two available batch processors, the Batch Code Processor , called with the @batchcode command. This is a very powerful tool. It allows you to craft Python files to act as blueprints of your entire game world. These files have access to use Evennia’s Python API directly. Batchcode allows for easy editing and creation in whatever text editor you prefer, avoiding having to manually build the world line-by-line inside the game.

    Important warning: @batchcode’s power is only rivaled by the @py command. Batchcode is so powerful it should be reserved only for the superuser. Think carefully before you let others (such as Developer- level staff) run @batchcode on their own - make sure you are okay with them running arbitrary Python code on your server.

    @@ -612,7 +617,10 @@ Exits: north(#8), east(#9), south(#10), west(#11)
  • modules |
  • - + + + +
    @@ -244,7 +249,10 @@ is found in the modules | - + + + +
    @@ -148,7 +153,10 @@ the Wikipedia article modules | - + + + +
    @@ -47,7 +52,7 @@

    To see which colours your client support, use the default @color command. This will list all available colours for ANSI and Xterm256 along with the codes you use for them. You can find a list of all the parsed ANSI-colour codes in evennia/utils/ansi.py.

    ANSI colours

    -

    Evennia supports the ANSI standard for text. This is by far the most supported MUD-color standard, available in all but the most ancient mud clients. The ANSI colours are red, green, yellow, blue, magenta, cyan, white and black. They are abbreviated by their first letter except for black which is abbreviated with the letter x. In ANSI there are “bright” and “normal” (darker) versions of each color, adding up to a total of 16 colours to use for foreground text. There are also 8 “background” colours. These have no bright alternative in ANSI (but Evennia uses the Xterm256 extension behind the scenes to offer them anyway).

    +

    Evennia supports the ANSI standard for text. This is by far the most supported MUD-color standard, available in all but the most ancient mud clients. The ANSI colours are red, green, yellow, blue, magenta, cyan, white and black. They are abbreviated by their first letter except for black which is abbreviated with the letter x. In ANSI there are “bright” and “normal” (darker) versions of each color, adding up to a total of 16 colours to use for foreground text. There are also 8 “background” colours. These have no bright alternative in ANSI (but Evennia uses the Xterm256 extension behind the scenes to offer them anyway).

    To colour your text you put special tags in it. Evennia will parse these and convert them to the correct markup for the client used. If the user’s client/console/display supports ANSI colour, they will see the text in the specified colour, otherwise the tags will be stripped (uncolored text). This works also for non-terminal clients, such as the webclient. For the webclient, Evennia will translate the codes to HTML RGB colors.

    Here is an example of the tags in action:

     |rThis text is bright red.|n This is normal text.
    @@ -68,7 +73,7 @@ These are normal-intensity and are thus always given as uppercase, such as
     
  • |H negates the effects |h and returns all ANSI foreground colors (|! and | types) to ‘normal’ intensity. It has no effect on background and Xterm colors.

  • -

    Note: The ANSI standard does not actually support bright backgrounds like |[r - the standard only supports “normal” intensity backgrounds. To get around this Evennia instead implements these as Xterm256 colours behind the scenes. If the client does not support Xterm256 the ANSI colors will be used instead and there will be no visible difference between using upper- and lower-case background tags.

    +

    Note: The ANSI standard does not actually support bright backgrounds like |[r - the standard only supports “normal” intensity backgrounds. To get around this Evennia instead implements these as Xterm256 colours behind the scenes. If the client does not support Xterm256 the ANSI colors will be used instead and there will be no visible difference between using upper- and lower-case background tags.

    If you want to display an ANSI marker as output text (without having any effect), you need to escape it by preceding its | with another |:

    say The ||r ANSI marker changes text color to bright red.
    @@ -351,7 +356,10 @@ These are normal-intensity and are thus always given as uppercase, such as
             
  • modules |
  • - + + + +
    @@ -172,7 +177,10 @@
  • modules |
  • - + + + +
    @@ -833,7 +838,10 @@ show others what’s going on.

  • modules |
  • - + + + +
    @@ -42,7 +47,7 @@

    What we will need is the following:

    • An NPC typeclass that can react when someone enters.

    • -
    • A custom Room typeclass that can tell the NPC that someone entered.

    • +
    • A custom Room typeclass that can tell the NPC that someone entered.

    • We will also tweak our default Character typeclass a little.

    To begin with, we need to create an NPC typeclass. Create a new file inside of your typeclasses folder and name it npcs.py and then add the following code:

    @@ -200,7 +205,10 @@
  • modules |
  • - + + + +
    @@ -213,7 +218,10 @@
  • modules |
  • - + + + +
    @@ -39,10 +44,10 @@

    You will often want to operate on a specific object in the database. For example when a player attacks a named target you’ll need to find that target so it can be attacked. Or when a rain storm draws in you need to find all outdoor-rooms so you can show it raining in them. This tutorial explains Evennia’s tools for searching.

    Things to search for

    -

    The first thing to consider is the base type of the thing you are searching for. Evennia organizes its database into a few main tables: Objects, Accounts, Scripts, Channels, Messages and Help Entries. Most of the time you’ll likely spend your time searching for Objects and the occasional Accounts.

    +

    The first thing to consider is the base type of the thing you are searching for. Evennia organizes its database into a few main tables: Objects, Accounts, Scripts, Channels, Messages and Help Entries. Most of the time you’ll likely spend your time searching for Objects and the occasional Accounts.

    So to find an entity, what can be searched for?

      -
    • The key is the name of the entity. While you can get this from obj.key the database field is actually named obj.db_key - this is useful to know only when you do direct database queries. The one exception is Accounts, where the database field for .key is instead named username (this is a Django requirement). When you don’t specify search-type, you’ll usually search based on key. Aliases are extra names given to Objects using something like @alias or obj.aliases.add('name'). The main search functions (see below) will automatically search for aliases whenever you search by-key.

    • +
    • The key is the name of the entity. While you can get this from obj.key the database field is actually named obj.db_key - this is useful to know only when you do direct database queries. The one exception is Accounts, where the database field for .key is instead named username (this is a Django requirement). When you don’t specify search-type, you’ll usually search based on key. Aliases are extra names given to Objects using something like @alias or obj.aliases.add('name'). The main search functions (see below) will automatically search for aliases whenever you search by-key.

    • Tags are the main way to group and identify objects in Evennia. Tags can most often be used (sometimes together with keys) to uniquely identify an object. For example, even though you have two locations with the same name, you can separate them by their tagging (this is how Evennia implements ‘zones’ seen in other systems). Tags can also have categories, to further organize your data for quick lookups.

    • An object’s Attributes can also used to find an object. This can be very useful but since Attributes can store almost any data they are far less optimized to search for than Tags or keys.

    • The object’s Typeclass indicate the sub-type of entity. A Character, Flower or Sword are all types of Objects. A Bot is a kind of Account. The database field is called typeclass_path and holds the full Python-path to the class. You can usually specify the typeclass as an argument to Evennia’s search functions as well as use the class directly to limit queries.

    • @@ -434,7 +439,10 @@ Next we filter on this annotation, using the name modules | - + + + +
    @@ -224,7 +229,10 @@
  • modules |
  • - + + + + @@ -76,7 +81,7 @@

    Entering and leaving the train

    -

    Using the @telcommand like shown above is obviously not what we want. @tel is an admin command and normal players will thus never be able to enter the train! It is also not really a good idea to use Exits to get in and out of the train - Exits are (at least by default) objects too. They point to a specific destination. If we put an Exit in this room leading inside the train it would stay here when the train moved away (still leading into the train like a magic portal!). In the same way, if we put an Exit object inside the train, it would always point back to this room, regardless of where the Train has moved. Now, one could define custom Exit types that move with the train or change their destination in the right way - but this seems to be a pretty cumbersome solution.

    +

    Using the @telcommand like shown above is obviously not what we want. @tel is an admin command and normal players will thus never be able to enter the train! It is also not really a good idea to use Exits to get in and out of the train - Exits are (at least by default) objects too. They point to a specific destination. If we put an Exit in this room leading inside the train it would stay here when the train moved away (still leading into the train like a magic portal!). In the same way, if we put an Exit object inside the train, it would always point back to this room, regardless of where the Train has moved. Now, one could define custom Exit types that move with the train or change their destination in the right way - but this seems to be a pretty cumbersome solution.

    What we will do instead is to create some new commands: one for entering the train and another for leaving it again. These will be stored on the train object and will thus be made available to whomever is either inside it or in the same room as the train.

    Let’s create a new command module as mygame/commands/train.py:

     1
    @@ -506,7 +511,7 @@
     
  • Make it impossible to exit and enter the train mid-ride. This could be made by having the enter/exit commands check so the train is not moving before allowing the caller to proceed.

  • Have train conductor commands that can override the automatic start/stop.

  • Allow for in-between stops between the start- and end station

  • -
  • Have a rail road track instead of hard-coding the rooms in the train object. This could for example be a custom Exit only traversable by trains. The train will follow the track. Some track segments can split to lead to two different rooms and a player can switch the direction to which room it goes.

  • +
  • Have a rail road track instead of hard-coding the rooms in the train object. This could for example be a custom Exit only traversable by trains. The train will follow the track. Some track segments can split to lead to two different rooms and a player can switch the direction to which room it goes.

  • Create another kind of vehicle!

  • @@ -571,7 +576,10 @@
  • modules |
  • - + + + + @@ -162,7 +167,10 @@ itself.

  • modules |
  • - + + + + @@ -419,7 +424,7 @@ You +attack with a combat score of 12!

    NPC system

    Here we will re-use the Character class by introducing a command that can create NPC objects. We should also be able to set its Power and order it around.

    -

    There are a few ways to define the NPC class. We could in theory create a custom typeclass for it and put a custom NPC-specific cmdset on all NPCs. This cmdset could hold all manipulation commands. Since we expect NPC manipulation to be a common occurrence among the user base however, we will instead put all relevant NPC commands in the default command set and limit eventual access with Permissions and Locks.

    +

    There are a few ways to define the NPC class. We could in theory create a custom typeclass for it and put a custom NPC-specific cmdset on all NPCs. This cmdset could hold all manipulation commands. Since we expect NPC manipulation to be a common occurrence among the user base however, we will instead put all relevant NPC commands in the default command set and limit eventual access with Permissions and Locks.

    Creating an NPC with +createNPC

    We need a command for creating the NPC, this is a very straightforward command:

    @@ -505,7 +510,7 @@ You +attack with a combat score of 12! exclude=caller)
    -

    Here we define a +createnpc (+createNPC works too) that is callable by everyone not having the nonpcspermission” (in Evennia, a “permission” can just as well be used to block access, it depends on the lock we define). We create the NPC object in the caller’s current location, using our custom Character typeclass to do so.

    +

    Here we define a +createnpc (+createNPC works too) that is callable by everyone not having the nonpcspermission” (in Evennia, a “permission” can just as well be used to block access, it depends on the lock we define). We create the NPC object in the caller’s current location, using our custom Character typeclass to do so.

    We set an extra lock condition on the NPC, which we will use to check who may edit the NPC later – we allow the creator to do so, and anyone with the Builders permission (or higher). See Locks for more information about the lock system.

    Note that we just give the object default permissions (by not specifying the permissions keyword to the create_object() call). In some games one might want to give the NPC the same permissions as the Character creating them, this might be a security risk though.

    Add this command to your default cmdset the same way you did the +attack command earlier. @reload and it will be available to test.

    @@ -845,7 +850,10 @@ as the modules | - + + + +
    @@ -84,7 +89,7 @@
  • Tutorial: Making an NPC shop (also advanced EvMenu usage)

  • Tutorial: Implementing a Static In Game Map (also Batch Code usage)

  • Tutorial: Implementing a Dynamic In Game Map

  • -
  • Tutorial: Writing your own unit tests

  • +
  • Tutorial: Writing your own unit tests

  • @@ -37,7 +42,7 @@

    Typeclasses

    Typeclasses form the core of Evennia data storage. It allows Evennia to represent any number of different game entities as Python classes, without having to modify the database schema for every new type.

    -

    In Evennia the most important game entities, Accounts, Objects, Scripts and Channels are all Python classes inheriting, at varying distance, from evennia.typeclasses.models.TypedObject. In the documentation we refer to these objects as being “typeclassed” or even “being a typeclass”.

    +

    In Evennia the most important game entities, Accounts, Objects, Scripts and Channels are all Python classes inheriting, at varying distance, from evennia.typeclasses.models.TypedObject. In the documentation we refer to these objects as being “typeclassed” or even “being a typeclass”.

    This is how the inheritance looks for the typeclasses in Evennia:

                      TypedObject
           _________________|_________________________________
    @@ -69,7 +74,7 @@ to Evennia you must import that module from somewhere.

    Difference between typeclasses and classes

    All Evennia classes inheriting from class in the table above share one important feature and two important limitations. This is why we don’t simply call them “classes” but “typeclasses”.

      -
    1. A typeclass can save itself to the database. This means that some properties (actually not that many) on the class actually represents database fields and can only hold very specific data types. This is detailed below.

    2. +
    3. A typeclass can save itself to the database. This means that some properties (actually not that many) on the class actually represents database fields and can only hold very specific data types. This is detailed below.

    4. Due to its connection to the database, the typeclass’ name must be unique across the entire server namespace. That is, there must never be two same-named classes defined anywhere. So the below code would give an error (since DefaultObject is now globally found both in this module and in the default library):

      1
       2
      @@ -332,7 +337,10 @@ etc.

    5. modules |
    6. - + + + +
      @@ -184,7 +189,10 @@ push it over the limit, so to speak.

    7. modules |
    8. - + + + + @@ -479,7 +484,10 @@
    9. modules |
    10. - + + + + @@ -77,12 +82,12 @@
      -

      Hint: If the evennia command is not found, you most likely need to activate your virtualenv.

      +

      Hint: If the evennia command is not found, you most likely need to activate your virtualenv.

      Resetting your database

      -

      Should you ever want to start over completely from scratch, there is no need to re-download Evennia or anything like that. You just need to clear your database. Once you are done, you just rebuild it from scratch as described in step 2 of the Getting Started guide.

      +

      Should you ever want to start over completely from scratch, there is no need to re-download Evennia or anything like that. You just need to clear your database. Once you are done, you just rebuild it from scratch as described in step 2 of the Getting Started guide.

      First stop a running server with

      evennia stop
       
      @@ -172,7 +177,10 @@ you then just run e
    11. modules |
    12. - + + + +
      @@ -188,7 +193,10 @@
    13. modules |
    14. - + + + +
      @@ -113,7 +118,10 @@ You need to add this file to git ( modules | - + + + + @@ -232,7 +237,7 @@ password <my_github_password>

      Committing fixes to Evennia

      Contributing can mean both bug-fixes or adding new features to Evennia. Please note that if your change is not already listed and accepted in the Issue Tracker, it is recommended that you first hit the developer mailing list or IRC chat to see beforehand if your feature is deemed suitable to include as a core feature in the engine. When it comes to bug-fixes, other developers may also have good input on how to go about resolving the issue.

      -

      To contribute you need to have forked Evennia first. As described above you should do your modification in a separate local branch (not in the master branch). This branch is what you then present to us (as a Pull request, PR, see below). We can then merge your change into the upstream master and you then do git pull to update master usual. Now that the master is updated with your fixes, you can safely delete your local work branch. Below we describe this work flow.

      +

      To contribute you need to have forked Evennia first. As described above you should do your modification in a separate local branch (not in the master branch). This branch is what you then present to us (as a Pull request, PR, see below). We can then merge your change into the upstream master and you then do git pull to update master usual. Now that the master is updated with your fixes, you can safely delete your local work branch. Below we describe this work flow.

      First update the Evennia master branch to the latest Evennia version:

      git checkout master
       git pull upstream master
      @@ -401,7 +406,10 @@ password <my_github_password>
               
    15. modules |
    16. - + + + +
      @@ -133,7 +138,10 @@
    17. modules |
    18. - + + + +
      @@ -315,7 +320,7 @@

      Most importantly, the following attributes must be set on the created character object:

      @@ -299,7 +304,10 @@
    19. modules |
    20. - + + + + @@ -175,7 +180,10 @@ development you can try it at modules | - + + + + @@ -139,7 +144,10 @@
    21. modules |
    22. - + + + + @@ -318,7 +323,10 @@ It’s certainly a lot more flexible than what I had in mind.

    23. modules |
    24. - + + + + @@ -347,7 +352,10 @@ and
    25. modules |
    26. - + + + + @@ -239,7 +244,10 @@
    27. modules |
    28. - + + + + @@ -118,7 +123,10 @@
    29. modules |
    30. - + + + + @@ -1375,7 +1380,10 @@ modules | - + + + + @@ -331,7 +336,10 @@ modules | - + + + + @@ -1213,7 +1218,10 @@ modules | - + + + + @@ -277,7 +282,10 @@ modules | - + + + + @@ -904,7 +909,10 @@ modules | - + + + + @@ -412,7 +417,10 @@ modules | - + + + + @@ -1436,7 +1441,10 @@ modules | - + + + + @@ -509,7 +514,10 @@ modules | - + + + + @@ -545,7 +550,10 @@ modules | - + + + + @@ -1706,7 +1711,10 @@ >modules | - + + + + @@ -365,7 +370,10 @@ >modules | - + + + + @@ -657,7 +662,10 @@ >modules | - + + + + @@ -267,7 +272,10 @@ >modules | - + + + + @@ -259,7 +264,10 @@ >modules | - + + + + @@ -533,7 +538,10 @@ >modules | - + + + + @@ -888,7 +893,10 @@ >modules | - + + + + @@ -296,7 +301,10 @@ >modules | - + + + + @@ -709,7 +714,10 @@ >modules | - + + + + @@ -745,7 +750,10 @@ >modules | - + + + + @@ -750,7 +755,10 @@ >modules | - + + + + @@ -1090,7 +1095,10 @@ >modules | - + + + + @@ -673,7 +678,10 @@ >modules | - + + + + @@ -978,7 +983,10 @@ >modules | - + + + + @@ -3819,7 +3824,10 @@ >modules | - + + + + @@ -155,7 +160,10 @@ >modules | - + + + + @@ -170,7 +175,10 @@ >modules | - + + + + @@ -96,7 +101,10 @@ >modules | - + + + + @@ -105,7 +110,10 @@ >modules | - + + + + @@ -1401,7 +1406,10 @@ >modules | - + + + + @@ -793,7 +798,10 @@ >modules | - + + + + @@ -131,9 +136,7 @@ # matches (objs), suggestions (strs) return ( [mapping[match["ref"]] for match in matches], - [ - str(match["ref"]) for match in matches - ], # + f" (score {match['score']})") # good debug + [str(match["ref"]) for match in matches], # + f" (score {match['score']})") # good debug ) @@ -179,10 +182,7 @@ if type(self).help_more: usemore = True - if self.session and self.session.protocol_key in ( - "websocket", - "ajax/comet", - ): + if self.session and self.session.protocol_key in ("websocket", "ajax/comet",): try: options = self.account.db._saved_webclient_options if options and options["helppopup"]: @@ -216,9 +216,7 @@ if title: string += "|CHelp for |w%s|n" % title if aliases: - string += " |C(aliases: %s|C)|n" % ( - "|C,|n ".join("|w%s|n" % ali for ali in aliases) - ) + string += " |C(aliases: %s|C)|n" % ("|C,|n ".join("|w%s|n" % ali for ali in aliases)) if help_text: string += "\n%s" % dedent(help_text.rstrip()) if suggested: @@ -245,22 +243,15 @@ category_str = f"-- {category.title()} " grid.append( ANSIString( - category_clr - + category_str - + "-" * (width - len(category_str)) - + topic_clr + category_clr + category_str + "-" * (width - len(category_str)) + topic_clr ) ) verbatim_elements.append(len(grid) - 1) - entries = sorted( - set(hdict_cmds.get(category, []) + hdict_db.get(category, [])) - ) + entries = sorted(set(hdict_cmds.get(category, []) + hdict_db.get(category, []))) grid.extend(entries) - gridrows = format_grid( - grid, width, sep=" ", verbatim_elements=verbatim_elements - ) + gridrows = format_grid(grid, width, sep=" ", verbatim_elements=verbatim_elements) gridrows = ANSIString("\n").join(gridrows) return gridrows @@ -332,9 +323,7 @@ # retrieve all available commands and database topics all_cmds = [cmd for cmd in cmdset if self.check_show_help(cmd, caller)] all_topics = [ - topic - for topic in HelpEntry.objects.all() - if topic.access(caller, "view", default=True) + topic for topic in HelpEntry.objects.all() if topic.access(caller, "view", default=True) ] all_categories = list( set( @@ -359,11 +348,7 @@ return # Try to access a particular help entry or category - entries = ( - [cmd for cmd in all_cmds if cmd] - + list(HelpEntry.objects.all()) - + all_categories - ) + entries = [cmd for cmd in all_cmds if cmd] + list(HelpEntry.objects.all()) + all_categories for match_query in [f"{query}~1", f"{query}*"]: # We first do an exact word-match followed by a start-by query @@ -498,9 +483,7 @@ # check if we have an old entry with the same name try: for querystr in topicstrlist: - old_entry = HelpEntry.objects.find_topicmatch( - querystr - ) # also search by alias + old_entry = HelpEntry.objects.find_topicmatch(querystr) # also search by alias if old_entry: old_entry = list(old_entry)[0] break @@ -522,11 +505,7 @@ helpentry = old_entry else: helpentry = create.create_help_entry( - topicstr, - self.rhs, - category=category, - locks=lockstring, - aliases=aliases, + topicstr, self.rhs, category=category, locks=lockstring, aliases=aliases, ) self.caller.db._editing_help = helpentry @@ -543,9 +522,7 @@ if "append" in switches or "merge" in switches or "extend" in switches: # merge/append operations if not old_entry: - self.msg( - "Could not find topic '%s'. You must give an exact name." % topicstr - ) + self.msg("Could not find topic '%s'. You must give an exact name." % topicstr) return if not self.rhs: self.msg("You must supply text to append/merge.") @@ -592,9 +569,7 @@ topicstr, self.rhs, category=category, locks=lockstring, aliases=aliases ) if new_entry: - self.msg( - "Topic '%s'%s was successfully created." % (topicstr, aliastxt) - ) + self.msg("Topic '%s'%s was successfully created." % (topicstr, aliastxt)) if "edit" in switches: # open the line editor to edit the helptext self.caller.db._editing_help = new_entry @@ -609,8 +584,7 @@ return else: self.msg( - "Error when creating topic '%s'%s! Contact an admin." - % (topicstr, aliastxt) + "Error when creating topic '%s'%s! Contact an admin." % (topicstr, aliastxt) ) @@ -653,7 +627,10 @@ >modules | - + + + + @@ -354,7 +359,10 @@ >modules | - + + + + @@ -231,7 +236,10 @@ >modules | - + + + + @@ -1228,7 +1233,10 @@ >modules | - + + + + @@ -304,16 +309,17 @@
      [docs]class TestHelp(CommandTest): -
      [docs] def setUp(self): super().setUp() # we need to set up a logger here since lunr takes over the logger otherwise import logging + logging.basicConfig(level=logging.ERROR)
      [docs] def tearDown(self): super().tearDown() import logging + logging.disable(level=logging.ERROR)
      [docs] def test_help(self): @@ -1640,7 +1646,10 @@ >modules | - + + + +
      @@ -575,7 +580,10 @@ >modules | - + + + +
      @@ -567,7 +572,10 @@ >modules | - + + + + @@ -201,7 +206,10 @@ >modules | - + + + + @@ -401,7 +406,10 @@ >modules | - + + + + @@ -895,7 +900,10 @@ >modules | - + + + + @@ -493,7 +498,10 @@ >modules | - + + + + @@ -819,7 +824,10 @@ >modules | - + + + + @@ -96,7 +101,10 @@ >modules | - + + + + @@ -975,7 +980,10 @@ >modules | - + + + + @@ -1345,7 +1350,10 @@ >modules | - + + + + @@ -272,7 +277,10 @@ >modules | - + + + + @@ -822,7 +827,10 @@ >modules | - + + + + @@ -387,7 +392,10 @@ >modules | - + + + + @@ -340,7 +345,10 @@ >modules | - + + + + @@ -441,7 +446,10 @@ >modules | - + + + + @@ -671,7 +676,10 @@ >modules | - + + + + @@ -794,7 +799,10 @@ >modules | - + + + + @@ -231,7 +236,10 @@ >modules | - + + + + @@ -198,7 +203,10 @@ >modules | - + + + + @@ -303,7 +308,10 @@ >modules | - + + + + @@ -661,7 +666,10 @@ >modules | - + + + + @@ -169,7 +174,10 @@ >modules | - + + + + @@ -746,7 +751,10 @@ >modules | - + + + + @@ -621,7 +626,10 @@ >modules | - + + + + @@ -997,7 +1002,10 @@ >modules | - + + + + @@ -341,7 +346,10 @@ >modules | - + + + + @@ -437,7 +442,10 @@ >modules | - + + + + @@ -580,7 +585,10 @@ >modules | - + + + + @@ -332,7 +337,10 @@ >modules | - + + + + @@ -348,7 +353,10 @@ >modules | - + + + + @@ -650,7 +655,6 @@ passed in. """ - key = "use" aliases = "combine" locks = "cmd:pperm(use) or pperm(Player)" @@ -893,7 +897,10 @@ >modules | - + + + + @@ -432,7 +437,10 @@ >modules | - + + + + @@ -623,7 +628,10 @@ >modules | - + + + + @@ -1702,7 +1707,10 @@ >modules | - + + + + @@ -138,7 +143,10 @@ >modules | - + + + + @@ -327,7 +332,10 @@ >modules | - + + + + @@ -192,7 +197,10 @@ >modules | - + + + + @@ -250,7 +255,10 @@ >modules | - + + + + @@ -222,7 +227,10 @@ >modules | - + + + + @@ -211,7 +216,10 @@ >modules | - + + + + @@ -3387,7 +3392,10 @@ >modules | - + + + + @@ -655,7 +660,10 @@ >modules | - + + + + @@ -858,7 +863,10 @@ >modules | - + + + + @@ -1216,7 +1221,10 @@ >modules | - + + + + @@ -1535,7 +1540,10 @@ >modules | - + + + + @@ -1457,7 +1462,10 @@ >modules | - + + + + @@ -1513,7 +1518,10 @@ >modules | - + + + + @@ -144,7 +149,10 @@ >modules | - + + + + @@ -413,7 +418,10 @@ >modules | - + + + + @@ -245,7 +250,10 @@ >modules | - + + + + @@ -363,7 +368,10 @@ >modules | - + + + + @@ -149,7 +154,10 @@ >modules | - + + + + @@ -514,7 +519,10 @@ >modules | - + + + + @@ -1253,7 +1258,10 @@ >modules | - + + + + @@ -1188,7 +1193,10 @@ >modules | - + + + + @@ -373,7 +378,10 @@ >modules | - + + + + @@ -855,7 +860,10 @@ >modules | - + + + + @@ -266,7 +271,10 @@ >modules | - + + + + @@ -174,7 +179,10 @@ >modules | - + + + + @@ -97,7 +102,10 @@ >modules | - + + + + @@ -132,7 +137,10 @@ >modules | - + + + + @@ -141,7 +146,10 @@ >modules | - + + + + @@ -133,7 +138,10 @@ >modules | - + + + + @@ -102,7 +107,10 @@ >modules | - + + + + @@ -102,7 +107,10 @@ >modules | - + + + + @@ -115,7 +120,10 @@ >modules | - + + + + @@ -119,7 +124,10 @@ >modules | - + + + + @@ -134,7 +139,10 @@ >modules | - + + + + @@ -230,7 +235,10 @@ >modules | - + + + + @@ -371,7 +376,10 @@ >modules | - + + + + @@ -772,7 +777,10 @@ >modules | - + + + + @@ -829,7 +834,10 @@ >modules | - + + + + @@ -288,7 +293,10 @@ >modules | - + + + + @@ -275,7 +280,10 @@ >modules | - + + + + @@ -689,7 +694,10 @@ >modules | - + + + + @@ -463,7 +468,10 @@ >modules | - + + + + @@ -2737,7 +2742,10 @@ >modules | - + + + + @@ -267,7 +272,10 @@ >modules | - + + + + @@ -2837,7 +2842,10 @@ >modules | - + + + + @@ -424,7 +429,10 @@ >modules | - + + + + @@ -950,7 +955,10 @@ >modules | - + + + + @@ -1067,7 +1072,10 @@ >modules | - + + + + @@ -1153,7 +1158,10 @@ >modules | - + + + + @@ -169,7 +174,10 @@ >modules | - + + + + @@ -367,7 +372,10 @@ >modules | - + + + + @@ -259,7 +264,10 @@ >modules | - + + + + @@ -283,7 +288,10 @@ >modules | - + + + + @@ -250,7 +255,10 @@ >modules | - + + + + @@ -794,7 +799,10 @@ >modules | - + + + + @@ -281,7 +286,10 @@ >modules | - + + + + @@ -131,7 +136,10 @@ >modules | - + + + + @@ -707,7 +712,10 @@ >modules | - + + + + @@ -103,7 +108,10 @@ >modules | - + + + + @@ -330,7 +335,10 @@ >modules | - + + + + @@ -559,7 +564,10 @@ >modules | - + + + + @@ -202,7 +207,10 @@ >modules | - + + + + @@ -2375,7 +2380,10 @@ >modules | - + + + + @@ -257,7 +262,10 @@ >modules | - + + + + @@ -136,7 +141,10 @@ >modules | - + + + + @@ -317,7 +322,10 @@ >modules | - + + + + @@ -696,7 +701,10 @@ >modules | - + + + + @@ -131,7 +136,10 @@ >modules | - + + + + @@ -213,7 +218,10 @@ >modules | - + + + + @@ -569,7 +574,10 @@ >modules | - + + + + @@ -560,7 +565,10 @@ >modules | - + + + + @@ -437,7 +442,10 @@ >modules | - + + + + @@ -556,7 +561,10 @@ >modules | - + + + + @@ -168,7 +173,10 @@ >modules | - + + + + @@ -213,7 +218,10 @@ >modules | - + + + + @@ -162,7 +167,10 @@ >modules | - + + + + @@ -162,7 +167,10 @@ >modules | - + + + + @@ -491,7 +496,10 @@ >modules | - + + + + @@ -559,7 +564,10 @@ >modules | - + + + + @@ -241,7 +246,10 @@ >modules | - + + + + @@ -604,7 +609,10 @@ >modules | - + + + + @@ -195,7 +200,10 @@ >modules | - + + + + @@ -144,7 +149,10 @@ >modules | - + + + + @@ -564,7 +569,10 @@ >modules | - + + + + @@ -515,7 +520,10 @@ >modules | - + + + + @@ -233,7 +238,10 @@ >modules | - + + + + @@ -37,8 +42,7 @@

      Source code for evennia.server.portal.tests

      -
      -try:
      +try:
           from django.utils.unittest import TestCase
       except ImportError:
           from django.test import TestCase
      @@ -329,7 +333,7 @@
               self.proto.sessionhandler = PORTAL_SESSIONS
               self.proto.sessionhandler.portal = Mock()
               self.proto.transport = proto_helpers.StringTransport()
      -        #self.proto.transport = proto_helpers.FakeDatagramTransport()
      +        # self.proto.transport = proto_helpers.FakeDatagramTransport()
               self.proto.transport.client = ["localhost"]
               self.proto.transport.setTcpKeepAlive = Mock()
               self.proto.state = MagicMock()
      @@ -357,7 +361,7 @@
               self.proto.sendLine = MagicMock()
               msg = json.dumps(["logged_in", (), {}])
               self.proto.sessionhandler.data_out(self.proto, text=[["Excepting Alice"], {}])
      -        self.proto.sendLine.assert_called_with(json.dumps(['text', ['Excepting Alice'], {}]))
      + self.proto.sendLine.assert_called_with(json.dumps(["text", ["Excepting Alice"], {}])) @@ -399,7 +403,10 @@ >modules | - + + + + @@ -264,7 +269,10 @@ >modules | - + + + + @@ -375,7 +380,10 @@ >modules | - + + + + @@ -535,7 +540,10 @@ >modules | - + + + + @@ -512,7 +517,10 @@ >modules | - + + + + @@ -365,7 +370,10 @@ >modules | - + + + + @@ -194,7 +199,10 @@ >modules | - + + + + @@ -121,7 +126,10 @@ >modules | - + + + + @@ -240,7 +245,10 @@ >modules | - + + + + @@ -118,7 +123,10 @@ >modules | - + + + + @@ -171,7 +176,7 @@ else: # adjust the runtime not with 60s but with the actual elapsed time # in case this may varies slightly from 60s. - _GAMETIME_MODULE.SERVER_RUNTIME += (now - _LAST_SERVER_TIME_SNAPSHOT) + _GAMETIME_MODULE.SERVER_RUNTIME += now - _LAST_SERVER_TIME_SNAPSHOT _LAST_SERVER_TIME_SNAPSHOT = now # update game time and save it across reloads @@ -809,7 +814,10 @@ >modules | - + + + + @@ -503,7 +508,10 @@ >modules | - + + + + @@ -252,7 +257,10 @@ >modules | - + + + + @@ -926,7 +931,10 @@ >modules | - + + + + @@ -204,7 +209,10 @@ >modules | - + + + + @@ -96,7 +101,10 @@ >modules | - + + + + @@ -274,7 +279,10 @@ >modules | - + + + + @@ -204,7 +209,10 @@ >modules | - + + + + @@ -341,7 +346,10 @@ >modules | - + + + + @@ -107,7 +112,10 @@ >modules | - + + + + @@ -196,7 +201,10 @@ >modules | - + + + + @@ -168,7 +173,10 @@ >modules | - + + + + @@ -372,7 +377,10 @@ >modules | - + + + + @@ -422,7 +427,10 @@ >modules | - + + + + @@ -201,6 +206,7 @@ """ This attribute is stored via Django. Most Attributes will be using this class. """ + # # Attribute Database Model setup # @@ -308,10 +314,12 @@ value = property(__value_get, __value_set, __value_del) + # # Handlers making use of the Attribute model # +
      [docs]class IAttributeBackend: """ Abstract interface for the backends used by the Attribute Handler. @@ -375,8 +383,10 @@ if not _TYPECLASS_AGGRESSIVE_CACHE: return attrs = self.query_all() - self._cache = {f"{to_str(attr.key).lower()}-{attr.category.lower() if attr.category else None}": attr - for attr in attrs} + self._cache = { + f"{to_str(attr.key).lower()}-{attr.category.lower() if attr.category else None}": attr + for attr in attrs + } self._cache_complete = True
      [docs] def _get_cache_key(self, key, category): @@ -662,7 +672,9 @@ # update an existing attribute object self.do_batch_update_attribute(attr_obj, category, lockstring, new_value, strattr) else: - new_attr = self.do_create_attribute(keystr, category, lockstring, new_value, strvalue=strattr) + new_attr = self.do_create_attribute( + keystr, category, lockstring, new_value, strvalue=strattr + ) new_attrobjs.append(new_attr) if new_attrobjs: self.do_batch_finish(new_attrobjs)
      @@ -735,8 +747,13 @@ attrs = self._cache.values() if accessing_obj: - self.do_batch_delete([attr for attr in attrs if attr.access(accessing_obj, self._attredit, - default=default_access)]) + self.do_batch_delete( + [ + attr + for attr in attrs + if attr.access(accessing_obj, self._attredit, default=default_access) + ] + ) else: # have to cast the results to a list or we'll get a RuntimeError for removing from the dict we're iterating self.do_batch_delete(list(attrs)) @@ -804,7 +821,9 @@ strvalue has no meaning for InMemory attributes. """ - new_attr = self._attrclass(pk=self._next_id(), key=key, category=category, lock_storage=lockstring, value=value) + new_attr = self._attrclass( + pk=self._next_id(), key=key, category=category, lock_storage=lockstring, value=value + ) self._storage[(key, category)] = new_attr self._category_storage[category].append(new_attr) return new_attr @@ -844,6 +863,7 @@ """ Uses Django models for storing Attributes. """ + _attrclass = Attribute _m2m_fieldname = "db_attributes" @@ -883,9 +903,7 @@ } return [ conn.attribute - for conn in getattr(self.obj, self._m2m_fieldname).through.objects.filter( - **query - ) + for conn in getattr(self.obj, self._m2m_fieldname).through.objects.filter(**query) ]
      [docs] def do_create_attribute(self, key, category, lockstring, value, strvalue): @@ -894,7 +912,7 @@ "db_category": category, "db_model": self._model, "db_lock_storage": lockstring if lockstring else "", - "db_attrtype": self._attrtype + "db_attrtype": self._attrtype, } if strvalue: kwargs["db_value"] = None @@ -940,6 +958,7 @@ """ Handler for adding Attributes to the object. """ + _attrcreate = "attrcreate" _attredit = "attredit" _attrread = "attrread" @@ -1562,7 +1581,10 @@ >modules | - + + + +
      @@ -70,14 +75,7 @@ # Attribute manager methods
      [docs] def get_attribute( - self, - key=None, - category=None, - value=None, - strvalue=None, - obj=None, - attrtype=None, - **kwargs + self, key=None, category=None, value=None, strvalue=None, obj=None, attrtype=None, **kwargs ): """ Return Attribute objects by key, by category, by value, by @@ -121,9 +119,9 @@ # no reason to make strvalue/value mutually exclusive at this level query.append(("attribute__db_value", value)) return Attribute.objects.filter( - pk__in=self.model.db_attributes.through.objects.filter( - **dict(query) - ).values_list("attribute_id", flat=True) + pk__in=self.model.db_attributes.through.objects.filter(**dict(query)).values_list( + "attribute_id", flat=True + ) )
      [docs] def get_nick(self, key=None, category=None, value=None, strvalue=None, obj=None): @@ -150,13 +148,7 @@ )
      [docs] def get_by_attribute( - self, - key=None, - category=None, - value=None, - strvalue=None, - attrtype=None, - **kwargs + self, key=None, category=None, value=None, strvalue=None, attrtype=None, **kwargs ): """ Return objects having attributes with the given key, category, @@ -213,15 +205,11 @@ obj (list): Objects having the matching Nicks. """ - return self.get_by_attribute( - key=key, category=category, strvalue=nick, attrtype="nick" - )
      + return self.get_by_attribute(key=key, category=category, strvalue=nick, attrtype="nick") # Tag manager methods -
      [docs] def get_tag( - self, key=None, category=None, obj=None, tagtype=None, global_search=False - ): +
      [docs] def get_tag(self, key=None, category=None, obj=None, tagtype=None, global_search=False): """ Return Tag objects by key, by category, by object (it is stored on) or with a combination of those criteria. @@ -265,9 +253,9 @@ if category: query.append(("tag__db_category", category)) return Tag.objects.filter( - pk__in=self.model.db_tags.through.objects.filter( - **dict(query) - ).values_list("tag_id", flat=True) + pk__in=self.model.db_tags.through.objects.filter(**dict(query)).values_list( + "tag_id", flat=True + ) )
      [docs] def get_permission(self, key=None, category=None, obj=None): @@ -349,9 +337,7 @@ dbmodel = self.model.__dbclass__.__name__.lower() query = ( - self.filter( - db_tags__db_tagtype__iexact=tagtype, db_tags__db_model__iexact=dbmodel - ) + self.filter(db_tags__db_tagtype__iexact=tagtype, db_tags__db_model__iexact=dbmodel) .distinct() .order_by("id") ) @@ -371,9 +357,7 @@ clauses = Q() for ikey, key in enumerate(keys): # ANY mode; must match any one of the given tags/categories - clauses |= Q( - db_key__iexact=key, db_category__iexact=categories[ikey] - ) + clauses |= Q(db_key__iexact=key, db_category__iexact=categories[ikey]) else: # only one or more categories given clauses = Q() @@ -383,8 +367,7 @@ tags = _Tag.objects.filter(clauses) query = query.filter(db_tags__in=tags).annotate( - matches=Count("db_tags__pk", filter=Q(db_tags__in=tags), - distinct=True) + matches=Count("db_tags__pk", filter=Q(db_tags__in=tags), distinct=True) ) if anymatch: @@ -451,9 +434,7 @@ # try to get old tag dbmodel = self.model.__dbclass__.__name__.lower() - tag = self.get_tag( - key=key, category=category, tagtype=tagtype, global_search=True - ) + tag = self.get_tag(key=key, category=category, tagtype=tagtype, global_search=True) if tag and data is not None: # get tag from list returned by get_tag tag = tag[0] @@ -467,9 +448,7 @@ from evennia.typeclasses.models import Tag as _Tag tag = _Tag.objects.create( db_key=key.strip().lower() if key is not None else None, - db_category=category.strip().lower() - if category and key is not None - else None, + db_category=category.strip().lower() if category and key is not None else None, db_data=data, db_model=dbmodel, db_tagtype=tagtype.strip().lower() if tagtype is not None else None, @@ -578,8 +557,7 @@ typeclass=F("db_typeclass_path"), # Calculate this class' percentage of total composition percent=ExpressionWrapper( - ((F("count") / float(self.count())) * 100.0), - output_field=FloatField(), + ((F("count") / float(self.count())) * 100.0), output_field=FloatField(), ), ) .values("typeclass", "count", "percent") @@ -599,9 +577,7 @@ stats = self.get_typeclass_totals().order_by("typeclass") return {x.get("typeclass"): x.get("count") for x in stats}
      - @@ -75,7 +80,12 @@ from django.utils.encoding import smart_str from django.utils.text import slugify -from evennia.typeclasses.attributes import Attribute, AttributeHandler, ModelAttributeBackend, InMemoryAttributeBackend +from evennia.typeclasses.attributes import ( + Attribute, + AttributeHandler, + ModelAttributeBackend, + InMemoryAttributeBackend, +) from evennia.typeclasses.attributes import DbHolder from evennia.typeclasses.tags import Tag, TagHandler, AliasHandler, PermissionHandler @@ -161,6 +171,7 @@ signals.pre_delete.connect(remove_attributes_on_delete, sender=new_class) return new_class + # # Main TypedObject abstraction # @@ -1050,7 +1061,10 @@ >modules | - + + + +
      @@ -574,7 +579,10 @@ >modules | - + + + + @@ -97,16 +102,12 @@ self.obj2.tags.add("tag4") self.obj2.tags.add("tag2c") self.assertEqual(self._manager("get_by_tag", "tag1"), [self.obj1]) - self.assertEqual( - set(self._manager("get_by_tag", "tag2")), set([self.obj1, self.obj2]) - ) + self.assertEqual(set(self._manager("get_by_tag", "tag2")), set([self.obj1, self.obj2])) self.assertEqual(self._manager("get_by_tag", "tag2a"), [self.obj2]) self.assertEqual(self._manager("get_by_tag", "tag3 with spaces"), [self.obj2]) self.assertEqual(self._manager("get_by_tag", ["tag2a", "tag2b"]), [self.obj2]) self.assertEqual(self._manager("get_by_tag", ["tag2a", "tag1"]), []) - self.assertEqual( - self._manager("get_by_tag", ["tag2a", "tag4", "tag2c"]), [self.obj2] - ) + self.assertEqual(self._manager("get_by_tag", ["tag2a", "tag4", "tag2c"]), [self.obj2])
      [docs] def test_get_by_tag_and_category(self): self.obj1.tags.add("tag5", "category1") @@ -122,24 +123,17 @@ self.obj1.tags.add("tag8", "category6") self.obj2.tags.add("tag9", "category6") - self.assertEqual( - self._manager("get_by_tag", "tag5", "category1"), [self.obj1, self.obj2] - ) + self.assertEqual(self._manager("get_by_tag", "tag5", "category1"), [self.obj1, self.obj2]) self.assertEqual(self._manager("get_by_tag", "tag6", "category1"), []) - self.assertEqual( - self._manager("get_by_tag", "tag6", "category3"), [self.obj1, self.obj2] - ) + self.assertEqual(self._manager("get_by_tag", "tag6", "category3"), [self.obj1, self.obj2]) self.assertEqual( self._manager("get_by_tag", ["tag5", "tag6"], ["category1", "category3"]), [self.obj1, self.obj2], ) self.assertEqual( - self._manager("get_by_tag", ["tag5", "tag7"], "category1"), - [self.obj1, self.obj2], - ) - self.assertEqual( - self._manager("get_by_tag", category="category1"), [self.obj1, self.obj2] + self._manager("get_by_tag", ["tag5", "tag7"], "category1"), [self.obj1, self.obj2], ) + self.assertEqual(self._manager("get_by_tag", category="category1"), [self.obj1, self.obj2]) self.assertEqual(self._manager("get_by_tag", category="category2"), [self.obj2]) self.assertEqual( self._manager("get_by_tag", category=["category1", "category3"]), @@ -149,40 +143,28 @@ self._manager("get_by_tag", category=["category1", "category2"]), [self.obj1, self.obj2], ) - self.assertEqual( - self._manager("get_by_tag", category=["category5", "category4"]), [] - ) - self.assertEqual( - self._manager("get_by_tag", category="category1"), [self.obj1, self.obj2] - ) - self.assertEqual( - self._manager("get_by_tag", category="category6"), [self.obj1, self.obj2] - )
      + self.assertEqual(self._manager("get_by_tag", category=["category5", "category4"]), []) + self.assertEqual(self._manager("get_by_tag", category="category1"), [self.obj1, self.obj2]) + self.assertEqual(self._manager("get_by_tag", category="category6"), [self.obj1, self.obj2])
      [docs] def test_get_tag_with_all(self): self.obj1.tags.add("tagA", "categoryA") self.assertEqual( - self._manager( - "get_by_tag", ["tagA", "tagB"], ["categoryA", "categoryB"], match="all" - ), + self._manager("get_by_tag", ["tagA", "tagB"], ["categoryA", "categoryB"], match="all"), [], )
      [docs] def test_get_tag_with_any(self): self.obj1.tags.add("tagA", "categoryA") self.assertEqual( - self._manager( - "get_by_tag", ["tagA", "tagB"], ["categoryA", "categoryB"], match="any" - ), + self._manager("get_by_tag", ["tagA", "tagB"], ["categoryA", "categoryB"], match="any"), [self.obj1], )
      [docs] def test_get_tag_withnomatch(self): self.obj1.tags.add("tagC", "categoryC") self.assertEqual( - self._manager( - "get_by_tag", ["tagA", "tagB"], ["categoryA", "categoryB"], match="any" - ), + self._manager("get_by_tag", ["tagA", "tagB"], ["categoryA", "categoryB"], match="any"), [], )
      @@ -226,7 +208,10 @@ >modules | - + + + + @@ -1474,7 +1479,10 @@ >modules | - + + + + @@ -508,7 +513,10 @@ >modules | - + + + + @@ -320,7 +325,10 @@ >modules | - + + + + @@ -659,7 +664,10 @@ >modules | - + + + + @@ -817,7 +822,10 @@ >modules | - + + + + @@ -1192,7 +1197,10 @@ >modules | - + + + + @@ -578,7 +583,10 @@ >modules | - + + + + @@ -1761,7 +1766,10 @@ >modules | - + + + + @@ -567,7 +572,10 @@ >modules | - + + + + @@ -1835,7 +1840,10 @@ >modules | - + + + + @@ -346,7 +351,10 @@ >modules | - + + + + @@ -111,7 +116,10 @@ >modules | - + + + + @@ -751,7 +756,10 @@ >modules | - + + + + @@ -156,7 +161,10 @@ >modules | - + + + + @@ -639,7 +644,10 @@ >modules | - + + + + @@ -612,7 +617,10 @@ >modules | - + + + + @@ -401,7 +406,10 @@ >modules | - + + + + @@ -258,7 +263,10 @@ >modules | - + + + + @@ -371,7 +376,10 @@ >modules | - + + + + @@ -432,7 +437,10 @@ >modules | - + + + + @@ -197,11 +202,13 @@ self.account2.delete() super().tearDown() +
      [docs]class LocalEvenniaTest(EvenniaTest): """ This test class is intended for inheriting in mygame tests. It helps ensure your tests are run with your own objects. """ + account_typeclass = settings.BASE_ACCOUNT_TYPECLASS object_typeclass = settings.BASE_OBJECT_TYPECLASS character_typeclass = settings.BASE_CHARACTER_TYPECLASS @@ -249,7 +256,10 @@ >modules | - + + + +
      @@ -299,7 +304,10 @@ >modules | - + + + + @@ -156,7 +161,10 @@ >modules | - + + + + @@ -396,7 +401,10 @@ >modules | - + + + + @@ -140,7 +145,10 @@ >modules | - + + + + @@ -342,7 +347,10 @@ >modules | - + + + + @@ -176,7 +181,10 @@ >modules | - + + + + @@ -420,7 +425,10 @@ >modules | - + + + + @@ -280,7 +285,10 @@ >modules | - + + + + @@ -413,7 +418,10 @@ >modules | - + + + + @@ -236,7 +241,10 @@ >modules | - + + + + @@ -447,7 +452,10 @@ >modules | - + + + + @@ -2379,7 +2384,10 @@ >modules | - + + + + @@ -315,7 +320,10 @@ >modules | - + + + + @@ -121,7 +126,10 @@ >modules | - + + + + @@ -176,7 +181,10 @@ >modules | - + + + + @@ -150,7 +155,10 @@ >modules | - + + + + @@ -149,7 +154,10 @@ >modules | - + + + + @@ -108,7 +113,10 @@ >modules | - + + + + @@ -250,7 +255,10 @@ >modules | - + + + + @@ -95,7 +100,10 @@ >modules | - + + + + @@ -367,7 +372,10 @@ >modules | - + + + + @@ -1199,7 +1204,10 @@ >modules | - + + + + @@ -925,7 +930,10 @@ modules | - + + + + @@ -306,7 +311,10 @@
    31. modules |
    32. - + + + + @@ -180,7 +185,10 @@ modules | - + + + + @@ -138,7 +143,10 @@ modules | - + + + + @@ -110,7 +115,10 @@ modules | - + + + + @@ -238,7 +243,10 @@ modules | - + + + + @@ -277,7 +282,10 @@
    33. modules |
    34. - + + + + @@ -2477,7 +2482,10 @@ class built by crea
    35. modules |
    36. - + + + + @@ -68,7 +73,7 @@ method. Otherwise all text will be returned to all connected sessions.

      Look in the ooc state.

      -_keyaliases = ('look', 'ls', 'l')
      +_keyaliases = ('l', 'ls', 'look')
      @@ -83,7 +88,7 @@ method. Otherwise all text will be returned to all connected sessions.

      -aliases = ['ls', 'l']
      +aliases = ['l', 'ls']
      @@ -114,7 +119,7 @@ method. Otherwise all text will be returned to all connected sessions.

      -search_index_entry = {'aliases': 'ls l', 'category': 'general', 'key': 'look', 'tags': '', 'text': '\n look while out-of-character\n\n Usage:\n look\n\n Look in the ooc state.\n '}
      +search_index_entry = {'aliases': 'l ls', 'category': 'general', 'key': 'look', 'tags': '', 'text': '\n look while out-of-character\n\n Usage:\n look\n\n Look in the ooc state.\n '}
      @@ -201,7 +206,7 @@ as you the account have access right to puppet it.

      This will leave your current character and put you in a incorporeal OOC state.

      -_keyaliases = ('unpuppet', 'ooc')
      +_keyaliases = ('ooc', 'unpuppet')
      @@ -469,7 +474,7 @@ settings. Note that saved options may not be able to be used if later connecting with a client with different capabilities.

      -_keyaliases = ('option', 'options')
      +_keyaliases = ('options', 'option')
      @@ -602,7 +607,7 @@ doing

      also for those with all permissions.

      -_keyaliases = ('doing', 'who')
      +_keyaliases = ('who', 'doing')
      @@ -766,7 +771,7 @@ use a higher-permission Character to escalate their permission level. Use the unquell command to revert back to normal operation.

      -_keyaliases = ('unquell', 'quell')
      +_keyaliases = ('quell', 'unquell')
      @@ -1054,7 +1059,7 @@ accidentally block innocent users connecting from the same country or region.

      -_keyaliases = ('ban', 'bans')
      +_keyaliases = ('bans', 'ban')
      @@ -1196,7 +1201,7 @@ limited forms of emit, for sending to rooms and to accounts respectively.

      -_keyaliases = ('pemit', 'emit', 'remit')
      +_keyaliases = ('emit', 'pemit', 'remit')
      @@ -1322,7 +1327,7 @@ account - set permission on an account (same as adding * to name)

      or account. If no permission is given, list all permissions on <object>.

      -_keyaliases = ('setperm', 'perm')
      +_keyaliases = ('perm', 'setperm')
      @@ -1540,7 +1545,7 @@ skipping, reloading etc.

      -aliases = ['batchcmd', 'batchcommand']
      +aliases = ['batchcommand', 'batchcmd']
      @@ -1571,7 +1576,7 @@ skipping, reloading etc.

      -search_index_entry = {'aliases': 'batchcmd batchcommand', 'category': 'building', 'key': 'batchcommands', 'tags': '', 'text': '\n build from batch-command file\n\n Usage:\n batchcommands[/interactive] <python.path.to.file>\n\n Switch:\n interactive - this mode will offer more control when\n executing the batch file, like stepping,\n skipping, reloading etc.\n\n Runs batches of commands from a batch-cmd text file (*.ev).\n\n '}
      +search_index_entry = {'aliases': 'batchcommand batchcmd', 'category': 'building', 'key': 'batchcommands', 'tags': '', 'text': '\n build from batch-command file\n\n Usage:\n batchcommands[/interactive] <python.path.to.file>\n\n Switch:\n interactive - this mode will offer more control when\n executing the batch file, like stepping,\n skipping, reloading etc.\n\n Runs batches of commands from a batch-cmd text file (*.ev).\n\n '}
      @@ -1603,7 +1608,7 @@ object copies behind when testing out the script.

      Runs batches of commands from a batch-code text file (*.py).

      -_keyaliases = ('batchcodes', 'batchcode')
      +_keyaliases = ('batchcode', 'batchcodes')
      @@ -1746,7 +1751,7 @@ changing the object in question, making those aliases usable by everyone.

      -_keyaliases = ('setobjalias', 'alias')
      +_keyaliases = ('alias', 'setobjalias')
      @@ -2131,7 +2136,7 @@ object of this type like this:

      describe the current room.

      -_keyaliases = ('describe', 'desc')
      +_keyaliases = ('desc', 'describe')
      @@ -2712,7 +2717,7 @@ to a user. Defaults to yourself.

      rename an account.

      -_keyaliases = ('rename', 'name')
      +_keyaliases = ('name', 'rename')
      @@ -3046,7 +3051,7 @@ module is searched from the default typeclass directory, as defined in the server settings.

      -_keyaliases = ('swap', 'type', 'parent', 'update', 'typeclass')
      +_keyaliases = ('typeclass', 'parent', 'type', 'update', 'swap')
      @@ -3056,7 +3061,7 @@ server settings.

      -aliases = ['swap', 'type', 'parent', 'update']
      +aliases = ['swap', 'type', 'update', 'parent']
      @@ -3087,7 +3092,7 @@ server settings.

      -search_index_entry = {'aliases': 'swap type parent update', 'category': 'building', 'key': 'typeclass', 'tags': '', 'text': "\n set or change an object's typeclass\n\n Usage:\n typeclass[/switch] <object> [= typeclass.path]\n typeclass/prototype <object> = prototype_key\n\n typeclass/list/show [typeclass.path]\n swap - this is a shorthand for using /force/reset flags.\n update - this is a shorthand for using the /force/reload flag.\n\n Switch:\n show, examine - display the current typeclass of object (default) or, if\n given a typeclass path, show the docstring of that typeclass.\n update - *only* re-run at_object_creation on this object\n meaning locks or other properties set later may remain.\n reset - clean out *all* the attributes and properties on the\n object - basically making this a new clean object.\n force - change to the typeclass also if the object\n already has a typeclass of the same name.\n list - show available typeclasses. Only typeclasses in modules actually\n imported or used from somewhere in the code will show up here\n (those typeclasses are still available if you know the path)\n prototype - clean and overwrite the object with the specified\n prototype key - effectively making a whole new object.\n\n Example:\n type button = examples.red_button.RedButton\n type/prototype button=a red button\n\n If the typeclass_path is not given, the current object's typeclass is\n assumed.\n\n View or set an object's typeclass. If setting, the creation hooks of the\n new typeclass will be run on the object. If you have clashing properties on\n the old class, use /reset. By default you are protected from changing to a\n typeclass of the same name as the one you already have - use /force to\n override this protection.\n\n The given typeclass must be identified by its location using python\n dot-notation pointing to the correct module and class. If no typeclass is\n given (or a wrong typeclass is given). Errors in the path or new typeclass\n will lead to the old typeclass being kept. The location of the typeclass\n module is searched from the default typeclass directory, as defined in the\n server settings.\n\n "}
      +search_index_entry = {'aliases': 'swap type update parent', 'category': 'building', 'key': 'typeclass', 'tags': '', 'text': "\n set or change an object's typeclass\n\n Usage:\n typeclass[/switch] <object> [= typeclass.path]\n typeclass/prototype <object> = prototype_key\n\n typeclass/list/show [typeclass.path]\n swap - this is a shorthand for using /force/reset flags.\n update - this is a shorthand for using the /force/reload flag.\n\n Switch:\n show, examine - display the current typeclass of object (default) or, if\n given a typeclass path, show the docstring of that typeclass.\n update - *only* re-run at_object_creation on this object\n meaning locks or other properties set later may remain.\n reset - clean out *all* the attributes and properties on the\n object - basically making this a new clean object.\n force - change to the typeclass also if the object\n already has a typeclass of the same name.\n list - show available typeclasses. Only typeclasses in modules actually\n imported or used from somewhere in the code will show up here\n (those typeclasses are still available if you know the path)\n prototype - clean and overwrite the object with the specified\n prototype key - effectively making a whole new object.\n\n Example:\n type button = examples.red_button.RedButton\n type/prototype button=a red button\n\n If the typeclass_path is not given, the current object's typeclass is\n assumed.\n\n View or set an object's typeclass. If setting, the creation hooks of the\n new typeclass will be run on the object. If you have clashing properties on\n the old class, use /reset. By default you are protected from changing to a\n typeclass of the same name as the one you already have - use /force to\n override this protection.\n\n The given typeclass must be identified by its location using python\n dot-notation pointing to the correct module and class. If no typeclass is\n given (or a wrong typeclass is given). Errors in the path or new typeclass\n will lead to the old typeclass being kept. The location of the typeclass\n module is searched from the default typeclass directory, as defined in the\n server settings.\n\n "}
      @@ -3260,7 +3265,7 @@ If object is not specified, the current location is examined.

      Append a * before the search string to examine an account.

      -_keyaliases = ('examine', 'exam', 'ex')
      +_keyaliases = ('exam', 'examine', 'ex')
      @@ -3360,7 +3365,7 @@ limits matches to within the given dbrefs range, or above/below if only one is given.

      -_keyaliases = ('locate', 'search', 'find')
      +_keyaliases = ('find', 'locate', 'search')
      @@ -3370,7 +3375,7 @@ one is given.

      -aliases = ['locate', 'search']
      +aliases = ['search', 'locate']
      @@ -3401,7 +3406,7 @@ one is given.

      -search_index_entry = {'aliases': 'locate search', 'category': 'building', 'key': 'find', 'tags': '', 'text': '\n search the database for objects\n\n Usage:\n find[/switches] <name or dbref or *account> [= dbrefmin[-dbrefmax]]\n locate - this is a shorthand for using the /loc switch.\n\n Switches:\n room - only look for rooms (location=None)\n exit - only look for exits (destination!=None)\n char - only look for characters (BASE_CHARACTER_TYPECLASS)\n exact - only exact matches are returned.\n loc - display object location if exists and match has one result\n startswith - search for names starting with the string, rather than containing\n\n Searches the database for an object of a particular name or exact #dbref.\n Use *accountname to search for an account. The switches allows for\n limiting object matches to certain game entities. Dbrefmin and dbrefmax\n limits matches to within the given dbrefs range, or above/below if only\n one is given.\n '}
      +search_index_entry = {'aliases': 'search locate', 'category': 'building', 'key': 'find', 'tags': '', 'text': '\n search the database for objects\n\n Usage:\n find[/switches] <name or dbref or *account> [= dbrefmin[-dbrefmax]]\n locate - this is a shorthand for using the /loc switch.\n\n Switches:\n room - only look for rooms (location=None)\n exit - only look for exits (destination!=None)\n char - only look for characters (BASE_CHARACTER_TYPECLASS)\n exact - only exact matches are returned.\n loc - display object location if exists and match has one result\n startswith - search for names starting with the string, rather than containing\n\n Searches the database for an object of a particular name or exact #dbref.\n Use *accountname to search for an account. The switches allows for\n limiting object matches to certain game entities. Dbrefmin and dbrefmax\n limits matches to within the given dbrefs range, or above/below if only\n one is given.\n '}
      @@ -3443,7 +3448,7 @@ reference. A puppeted object cannot be moved to None.

      teleported to the target location.

      -_keyaliases = ('teleport', 'tel')
      +_keyaliases = ('tel', 'teleport')
      @@ -3520,7 +3525,7 @@ object without specifying a script key/path will start/stop ALL scripts on the object.

      -_keyaliases = ('script', 'addscript')
      +_keyaliases = ('addscript', 'script')
      @@ -3596,7 +3601,7 @@ should be used with restrain - tags on their own are usually enough to for most grouping schemes.

      -_keyaliases = ('tags', 'tag')
      +_keyaliases = ('tag', 'tags')
      @@ -3748,7 +3753,7 @@ prototype-details.

      -_keyaliases = ('spawn', 'olc')
      +_keyaliases = ('olc', 'spawn')
      @@ -4011,7 +4016,7 @@ name. Subsequent calls of this command can be used to add multiple aliases to an already joined channel.

      -_keyaliases = ('addcom', 'chanalias', 'aliaschan')
      +_keyaliases = ('chanalias', 'aliaschan', 'addcom')
      @@ -4078,7 +4083,7 @@ unsubscribe. If the ‘all’ switch is used, remove all aliases for that channel.

      -_keyaliases = ('delchanalias', 'delcom', 'delaliaschan')
      +_keyaliases = ('delcom', 'delchanalias', 'delaliaschan')
      @@ -4211,7 +4216,7 @@ Use ‘comlist’ to only view your current channel subscriptions. Use addcom/delcom to join and leave channels

      -_keyaliases = ('clist', 'channels', 'comlist', 'channellist', 'chanlist', 'all channels')
      +_keyaliases = ('comlist', 'clist', 'channellist', 'chanlist', 'channels', 'all channels')
      @@ -4226,7 +4231,7 @@ Use addcom/delcom to join and leave channels

      -aliases = ['clist', 'comlist', 'channellist', 'chanlist', 'all channels']
      +aliases = ['comlist', 'clist', 'channellist', 'chanlist', 'all channels']
      @@ -4257,7 +4262,7 @@ Use addcom/delcom to join and leave channels

      -search_index_entry = {'aliases': 'clist comlist channellist chanlist all channels', 'category': 'comms', 'key': 'channels', 'tags': '', 'text': "\n list all channels available to you\n\n Usage:\n channels\n clist\n comlist\n\n Lists all channels available to you, whether you listen to them or not.\n Use 'comlist' to only view your current channel subscriptions.\n Use addcom/delcom to join and leave channels\n "}
      +search_index_entry = {'aliases': 'comlist clist channellist chanlist all channels', 'category': 'comms', 'key': 'channels', 'tags': '', 'text': "\n list all channels available to you\n\n Usage:\n channels\n clist\n comlist\n\n Lists all channels available to you, whether you listen to them or not.\n Use 'comlist' to only view your current channel subscriptions.\n Use addcom/delcom to join and leave channels\n "}
      @@ -4412,7 +4417,7 @@ they control it. It does not show the user’s name unless they provide the /sendername switch.

      -_keyaliases = ('cmsg', 'cemit')
      +_keyaliases = ('cemit', 'cmsg')
      @@ -5056,7 +5061,7 @@ look *<accou

      Observes your location or objects in your vicinity.

      -_keyaliases = ('look', 'ls', 'l')
      +_keyaliases = ('l', 'ls', 'look')
      @@ -5066,7 +5071,7 @@ look *<accou
      -aliases = ['ls', 'l']
      +aliases = ['l', 'ls']
      @@ -5102,7 +5107,7 @@ look *<accou
      -search_index_entry = {'aliases': 'ls l', 'category': 'general', 'key': 'look', 'tags': '', 'text': '\n look at location or object\n\n Usage:\n look\n look <obj>\n look *<account>\n\n Observes your location or objects in your vicinity.\n '}
      +search_index_entry = {'aliases': 'l ls', 'category': 'general', 'key': 'look', 'tags': '', 'text': '\n look at location or object\n\n Usage:\n look\n look <obj>\n look *<account>\n\n Observes your location or objects in your vicinity.\n '}
      @@ -5154,7 +5159,7 @@ are only available to you. If you want to permanently add keywords to an object for everyone to use, you need build privileges and the alias command.

      -_keyaliases = ('nick', 'nicks', 'nickname')
      +_keyaliases = ('nicks', 'nickname', 'nick')
      @@ -5224,7 +5229,7 @@ inv

      Shows your inventory.

      -_keyaliases = ('inventory', 'i', 'inv')
      +_keyaliases = ('i', 'inv', 'inventory')
      @@ -5549,7 +5554,7 @@ placing it in their inventory.

      Talk to those in your current location.

      -_keyaliases = ('"', 'say', "'")
      +_keyaliases = ('say', '"', "'")
      @@ -5675,7 +5680,7 @@ pose’s <pose text>

      automatically begin with your name.

      -_keyaliases = (':', 'pose', 'emote')
      +_keyaliases = (':', 'emote', 'pose')
      @@ -5744,7 +5749,7 @@ space.

      which permission groups you are a member of.

      -_keyaliases = ('access', 'hierarchy', 'groups')
      +_keyaliases = ('hierarchy', 'access', 'groups')
      @@ -6609,7 +6614,7 @@ affected. Non-persistent scripts will survive a reload (use reset to purge) and at_reload() hooks will be called.

      -_keyaliases = ('reload', 'restart')
      +_keyaliases = ('restart', 'reload')
      @@ -6676,7 +6681,7 @@ be called and any non-database saved scripts, ndb-attributes, cmdsets etc will be wiped.

      -_keyaliases = ('reboot', 'reset')
      +_keyaliases = ('reset', 'reboot')
      @@ -6831,7 +6836,7 @@ py evennia.managers.__doc__

      should only be accessible by trusted server admins/superusers.|n

      -_keyaliases = ('py', '!')
      +_keyaliases = ('!', 'py')
      @@ -6904,7 +6909,7 @@ required since whole classes of scripts often have the same name.

      Use script for managing commands on objects.

      -_keyaliases = ('scripts', 'listscripts', 'globalscript')
      +_keyaliases = ('globalscript', 'listscripts', 'scripts')
      @@ -6914,7 +6919,7 @@ required since whole classes of scripts often have the same name.

      -aliases = ['listscripts', 'globalscript']
      +aliases = ['globalscript', 'listscripts']
      @@ -6950,7 +6955,7 @@ required since whole classes of scripts often have the same name.

      -search_index_entry = {'aliases': 'listscripts globalscript', 'category': 'system', 'key': 'scripts', 'tags': '', 'text': '\n list and manage all running scripts\n\n Usage:\n scripts[/switches] [#dbref, key, script.path or <obj>]\n\n Switches:\n start - start a script (must supply a script path)\n stop - stops an existing script\n kill - kills a script - without running its cleanup hooks\n validate - run a validation on the script(s)\n\n If no switches are given, this command just views all active\n scripts. The argument can be either an object, at which point it\n will be searched for all scripts defined on it, or a script name\n or #dbref. For using the /stop switch, a unique script #dbref is\n required since whole classes of scripts often have the same name.\n\n Use script for managing commands on objects.\n '}
      +search_index_entry = {'aliases': 'globalscript listscripts', 'category': 'system', 'key': 'scripts', 'tags': '', 'text': '\n list and manage all running scripts\n\n Usage:\n scripts[/switches] [#dbref, key, script.path or <obj>]\n\n Switches:\n start - start a script (must supply a script path)\n stop - stops an existing script\n kill - kills a script - without running its cleanup hooks\n validate - run a validation on the script(s)\n\n If no switches are given, this command just views all active\n scripts. The argument can be either an object, at which point it\n will be searched for all scripts defined on it, or a script name\n or #dbref. For using the /stop switch, a unique script #dbref is\n required since whole classes of scripts often have the same name.\n\n Use script for managing commands on objects.\n '}
      @@ -6974,7 +6979,7 @@ a list of <nr> latest objects in database. If not given, <nr> defaults to 10.

      -_keyaliases = ('listobjects', 'objects', 'stats', 'listobjs', 'db')
      +_keyaliases = ('stats', 'db', 'listobjects', 'listobjs', 'objects')
      @@ -6984,7 +6989,7 @@ given, <nr> defaults to 10.

      -aliases = ['stats', 'listobjects', 'db', 'listobjs']
      +aliases = ['db', 'stats', 'listobjects', 'listobjs']
      @@ -7015,7 +7020,7 @@ given, <nr> defaults to 10.

      -search_index_entry = {'aliases': 'stats listobjects db listobjs', 'category': 'system', 'key': 'objects', 'tags': '', 'text': '\n statistics on objects in the database\n\n Usage:\n objects [<nr>]\n\n Gives statictics on objects in database as well as\n a list of <nr> latest objects in database. If not\n given, <nr> defaults to 10.\n '}
      +search_index_entry = {'aliases': 'db stats listobjects listobjs', 'category': 'system', 'key': 'objects', 'tags': '', 'text': '\n statistics on objects in the database\n\n Usage:\n objects [<nr>]\n\n Gives statictics on objects in database as well as\n a list of <nr> latest objects in database. If not\n given, <nr> defaults to 10.\n '}
      @@ -7241,7 +7246,7 @@ caches may not show you a lower Residual/Virtual memory footprint, the released memory will instead be re-used by the program.

      -_keyaliases = ('serverprocess', 'server', 'serverload')
      +_keyaliases = ('serverload', 'serverprocess', 'server')
      @@ -7251,7 +7256,7 @@ the released memory will instead be re-used by the program.

      -aliases = ['serverprocess', 'serverload']
      +aliases = ['serverload', 'serverprocess']
      @@ -7282,7 +7287,7 @@ the released memory will instead be re-used by the program.

      -search_index_entry = {'aliases': 'serverprocess serverload', 'category': 'system', 'key': 'server', 'tags': '', 'text': "\n show server load and memory statistics\n\n Usage:\n server[/mem]\n\n Switches:\n mem - return only a string of the current memory usage\n flushmem - flush the idmapper cache\n\n This command shows server load statistics and dynamic memory\n usage. It also allows to flush the cache of accessed database\n objects.\n\n Some Important statistics in the table:\n\n |wServer load|n is an average of processor usage. It's usually\n between 0 (no usage) and 1 (100% usage), but may also be\n temporarily higher if your computer has multiple CPU cores.\n\n The |wResident/Virtual memory|n displays the total memory used by\n the server process.\n\n Evennia |wcaches|n all retrieved database entities when they are\n loaded by use of the idmapper functionality. This allows Evennia\n to maintain the same instances of an entity and allowing\n non-persistent storage schemes. The total amount of cached objects\n are displayed plus a breakdown of database object types.\n\n The |wflushmem|n switch allows to flush the object cache. Please\n note that due to how Python's memory management works, releasing\n caches may not show you a lower Residual/Virtual memory footprint,\n the released memory will instead be re-used by the program.\n\n "}
      +search_index_entry = {'aliases': 'serverload serverprocess', 'category': 'system', 'key': 'server', 'tags': '', 'text': "\n show server load and memory statistics\n\n Usage:\n server[/mem]\n\n Switches:\n mem - return only a string of the current memory usage\n flushmem - flush the idmapper cache\n\n This command shows server load statistics and dynamic memory\n usage. It also allows to flush the cache of accessed database\n objects.\n\n Some Important statistics in the table:\n\n |wServer load|n is an average of processor usage. It's usually\n between 0 (no usage) and 1 (100% usage), but may also be\n temporarily higher if your computer has multiple CPU cores.\n\n The |wResident/Virtual memory|n displays the total memory used by\n the server process.\n\n Evennia |wcaches|n all retrieved database entities when they are\n loaded by use of the idmapper functionality. This allows Evennia\n to maintain the same instances of an entity and allowing\n non-persistent storage schemes. The total amount of cached objects\n are displayed plus a breakdown of database object types.\n\n The |wflushmem|n switch allows to flush the object cache. Please\n note that due to how Python's memory management works, releasing\n caches may not show you a lower Residual/Virtual memory footprint,\n the released memory will instead be re-used by the program.\n\n "}
      @@ -7934,7 +7939,7 @@ connect “account name” “pass word”

      If you have spaces in your name, enclose it in double quotes.

      -_keyaliases = ('conn', 'co', 'connect', 'con')
      +_keyaliases = ('connect', 'co', 'con', 'conn')
      @@ -7944,7 +7949,7 @@ connect “account name” “pass word”

      -aliases = ['co', 'conn', 'con']
      +aliases = ['co', 'con', 'conn']
      @@ -7984,7 +7989,7 @@ there is no object yet before the account has logged in)

      -search_index_entry = {'aliases': 'co conn con', 'category': 'general', 'key': 'connect', 'tags': '', 'text': '\n connect to the game\n\n Usage (at login screen):\n connect accountname password\n connect "account name" "pass word"\n\n Use the create command to first create an account before logging in.\n\n If you have spaces in your name, enclose it in double quotes.\n '}
      +search_index_entry = {'aliases': 'co con conn', 'category': 'general', 'key': 'connect', 'tags': '', 'text': '\n connect to the game\n\n Usage (at login screen):\n connect accountname password\n connect "account name" "pass word"\n\n Use the create command to first create an account before logging in.\n\n If you have spaces in your name, enclose it in double quotes.\n '}
      @@ -8128,7 +8133,7 @@ version is a bit more complicated.

      All it does is display the connect screen.

      -_keyaliases = ('look', 'l', '__unloggedin_look_command')
      +_keyaliases = ('l', '__unloggedin_look_command', 'look')
      @@ -8138,7 +8143,7 @@ All it does is display the connect screen.

      -aliases = ['look', 'l']
      +aliases = ['l', 'look']
      @@ -8169,7 +8174,7 @@ All it does is display the connect screen.

      -search_index_entry = {'aliases': 'look l', 'category': 'general', 'key': '__unloggedin_look_command', 'tags': '', 'text': '\n look when in unlogged-in state\n\n Usage:\n look\n\n This is an unconnected version of the look command for simplicity.\n\n This is called by the server and kicks everything in gear.\n All it does is display the connect screen.\n '}
      +search_index_entry = {'aliases': 'l look', 'category': 'general', 'key': '__unloggedin_look_command', 'tags': '', 'text': '\n look when in unlogged-in state\n\n Usage:\n look\n\n This is an unconnected version of the look command for simplicity.\n\n This is called by the server and kicks everything in gear.\n All it does is display the connect screen.\n '}
      @@ -8305,7 +8310,10 @@ for simplicity. It shows a pane of info.

    37. modules |
    38. - + + + + @@ -2728,7 +2733,10 @@ replace this without disabling auto_help.

    39. modules |
    40. - + + + + @@ -2398,7 +2403,10 @@ object the first time, the query is executed.

    41. modules |
    42. - + + + + @@ -216,7 +221,7 @@ your mind as long as the other party has not yet accepted. You can inspect the current offer using the ‘offers’ command.

      -_keyaliases = ('agree', 'accept')
      +_keyaliases = ('accept', 'agree')
      @@ -336,7 +341,7 @@ decline the old offer.

      determine if it’s worth your while.

      -_keyaliases = ('eval', 'evaluate')
      +_keyaliases = ('evaluate', 'eval')
      @@ -395,7 +400,7 @@ finish trade [:say]

      This ends the trade prematurely. No trade will take place.

      -_keyaliases = ('end trade', 'finish trade')
      +_keyaliases = ('finish trade', 'end trade')
      @@ -517,7 +522,7 @@ change your deal. You might also want to use ‘say’, ‘emote’ etc to try to influence the other part in the deal.

      -_keyaliases = ('offers', 'status', 'deal')
      +_keyaliases = ('status', 'offers', 'deal')
      @@ -582,7 +587,7 @@ optional say part works like the say command and allows you to add info to your choice.

      -_keyaliases = ('trade', 'barter')
      +_keyaliases = ('barter', 'trade')
      @@ -2086,7 +2091,7 @@ have an in-game existence, there is no concept of location or at them with this command.

      -_keyaliases = ('look', 'ls', 'l')
      +_keyaliases = ('l', 'ls', 'look')
      @@ -2096,7 +2101,7 @@ at them with this command.

      -aliases = ['ls', 'l']
      +aliases = ['l', 'ls']
      @@ -2133,7 +2138,7 @@ that is checked by the @ic command directly.

      -search_index_entry = {'aliases': 'ls l', 'category': 'general', 'key': 'look', 'tags': '', 'text': '\n ooc look\n\n Usage:\n look\n look <character>\n\n This is an OOC version of the look command. Since an Account doesn\'t\n have an in-game existence, there is no concept of location or\n "self".\n\n If any characters are available for you to control, you may look\n at them with this command.\n '}
      +search_index_entry = {'aliases': 'l ls', 'category': 'general', 'key': 'look', 'tags': '', 'text': '\n ooc look\n\n Usage:\n look\n look <character>\n\n This is an OOC version of the look command. Since an Account doesn\'t\n have an in-game existence, there is no concept of location or\n "self".\n\n If any characters are available for you to control, you may look\n at them with this command.\n '}
      @@ -2582,7 +2587,7 @@ inv

      Shows your inventory.

      -_keyaliases = ('inventory', 'i', 'inv')
      +_keyaliases = ('i', 'inv', 'inventory')
      @@ -3399,7 +3404,7 @@ the module given by settings.CONNECTION_SCREEN_MODULE.

      Use the create command to first create an account before logging in.

      -_keyaliases = ('conn', 'co', 'connect', 'con')
      +_keyaliases = ('connect', 'co', 'con', 'conn')
      @@ -3409,7 +3414,7 @@ the module given by settings.CONNECTION_SCREEN_MODULE.

      -aliases = ['co', 'conn', 'con']
      +aliases = ['co', 'con', 'conn']
      @@ -3444,7 +3449,7 @@ there is no object yet before the account has logged in)

      -search_index_entry = {'aliases': 'co conn con', 'category': 'general', 'key': 'connect', 'tags': '', 'text': '\n Connect to the game.\n\n Usage (at login screen):\n connect <email> <password>\n\n Use the create command to first create an account before logging in.\n '}
      +search_index_entry = {'aliases': 'co con conn', 'category': 'general', 'key': 'connect', 'tags': '', 'text': '\n Connect to the game.\n\n Usage (at login screen):\n connect <email> <password>\n\n Use the create command to first create an account before logging in.\n '}
      @@ -3581,7 +3586,7 @@ version is a bit more complicated.

      All it does is display the connect screen.

      -_keyaliases = ('look', 'l', '__unloggedin_look_command')
      +_keyaliases = ('l', '__unloggedin_look_command', 'look')
      @@ -3591,7 +3596,7 @@ All it does is display the connect screen.

      -aliases = ['look', 'l']
      +aliases = ['l', 'look']
      @@ -3622,7 +3627,7 @@ All it does is display the connect screen.

      -search_index_entry = {'aliases': 'look l', 'category': 'general', 'key': '__unloggedin_look_command', 'tags': '', 'text': '\n This is an unconnected version of the `look` command for simplicity.\n\n This is called by the server and kicks everything in gear.\n All it does is display the connect screen.\n '}
      +search_index_entry = {'aliases': 'l look', 'category': 'general', 'key': '__unloggedin_look_command', 'tags': '', 'text': '\n This is an unconnected version of the `look` command for simplicity.\n\n This is called by the server and kicks everything in gear.\n All it does is display the connect screen.\n '}
      @@ -3785,7 +3790,7 @@ timeslot. The available times are night, morning, afternoon and evening.

      version of the desc command.

      -_keyaliases = ('describe', 'desc')
      +_keyaliases = ('desc', 'describe')
      @@ -3983,7 +3988,7 @@ look *<account

      Observes your location, details at your location or objects in your vicinity.

      -_keyaliases = ('look', 'ls', 'l')
      +_keyaliases = ('l', 'ls', 'look')
      @@ -3993,7 +3998,7 @@ look *<account
      -aliases = ['ls', 'l']
      +aliases = ['l', 'ls']
      @@ -4019,7 +4024,7 @@ look *<account
      -search_index_entry = {'aliases': 'ls l', 'category': 'general', 'key': 'look', 'tags': '', 'text': '\n look\n\n Usage:\n look\n look <obj>\n look <room detail>\n look *<account>\n\n Observes your location, details at your location or objects in your vicinity.\n '}
      +search_index_entry = {'aliases': 'l ls', 'category': 'general', 'key': 'look', 'tags': '', 'text': '\n look\n\n Usage:\n look\n look <obj>\n look <room detail>\n look *<account>\n\n Observes your location, details at your location or objects in your vicinity.\n '}
      @@ -5523,7 +5528,7 @@ as plain text. Use e.g. ansi line break ||/ to add a new paragraph and + + or ansi space ||_ to add extra whitespace.

      -_keyaliases = ('+desc', 'desc')
      +_keyaliases = ('desc', '+desc')
      @@ -5756,7 +5761,7 @@ these objects are not needed anymore and can be deleted. Components of the puzzl will be re-created by use of the @armpuzzle command later.

      -_keyaliases = ('@puzzle', '@puzzlerecipe')
      +_keyaliases = ('@puzzlerecipe', '@puzzle')
      @@ -6062,7 +6067,7 @@ to all the variables defined therein.

      -_keyaliases = ('use', 'combine')
      +_keyaliases = ('combine', 'use')
      @@ -7124,7 +7129,7 @@ forget griatch

      Using the command without arguments will list all current recogs.

      -_keyaliases = ('recog', 'forget', 'recognize')
      +_keyaliases = ('recognize', 'forget', 'recog')
      @@ -7134,7 +7139,7 @@ Using the command without arguments will list all current recogs.

      -aliases = ['forget', 'recognize']
      +aliases = ['recognize', 'forget']
      @@ -7166,7 +7171,7 @@ Using the command without arguments will list all current recogs.

      -search_index_entry = {'aliases': 'forget recognize', 'category': 'general', 'key': 'recog', 'tags': '', 'text': '\n Recognize another person in the same room.\n\n Usage:\n recog\n recog sdesc as alias\n forget alias\n\n Example:\n recog tall man as Griatch\n forget griatch\n\n This will assign a personal alias for a person, or forget said alias.\n Using the command without arguments will list all current recogs.\n\n '}
      +search_index_entry = {'aliases': 'recognize forget', 'category': 'general', 'key': 'recog', 'tags': '', 'text': '\n Recognize another person in the same room.\n\n Usage:\n recog\n recog sdesc as alias\n forget alias\n\n Example:\n recog tall man as Griatch\n forget griatch\n\n This will assign a personal alias for a person, or forget said alias.\n Using the command without arguments will list all current recogs.\n\n '}
      @@ -7183,7 +7188,7 @@ Using the command without arguments will list all current recogs.

      Talk to those in your current location.

      -_keyaliases = ('"', 'say', "'")
      +_keyaliases = ('say', '"', "'")
      @@ -8233,7 +8238,7 @@ close <door>

      -_keyaliases = ('close', 'open')
      +_keyaliases = ('open', 'close')
      @@ -11292,7 +11297,10 @@ default one

    43. modules |
    44. - + + + + @@ -324,7 +329,7 @@ object’s callbacks, return an empty list.

      Command to edit callbacks.

      -_keyaliases = ('@call', '@calls', '@callback', '@callbacks')
      +_keyaliases = ('@calls', '@call', '@callback', '@callbacks')
      @@ -346,7 +351,7 @@ object’s callbacks, return an empty list.

      -aliases = ['@calls', '@callbacks', '@callback']
      +aliases = ['@calls', '@callback', '@callbacks']
      @@ -423,7 +428,7 @@ on user permission.

      -search_index_entry = {'aliases': '@calls @callbacks @callback', 'category': 'building', 'key': '@call', 'tags': '', 'text': '\n Command to edit callbacks.\n '}
      +search_index_entry = {'aliases': '@calls @callback @callbacks', 'category': 'building', 'key': '@call', 'tags': '', 'text': '\n Command to edit callbacks.\n '}
      @@ -1667,7 +1672,10 @@ temporary storage, waiting for the script to be initialized.

    45. modules |
    46. - + + + + @@ -254,7 +259,10 @@ information from strings.

    47. modules |
    48. - + + + + @@ -105,7 +110,10 @@
    49. modules |
    50. - + + + + @@ -230,7 +235,7 @@ the fight. If all participants in a fight disengage, the fight ends.

      -_keyaliases = ('disengage', 'spare')
      +_keyaliases = ('spare', 'disengage')
      @@ -339,7 +344,7 @@ When it’s your turn, you can attack other characters.

      if there are still any actions you can take.

      -_keyaliases = ('wait', 'hold', 'pass')
      +_keyaliases = ('hold', 'wait', 'pass')
      @@ -349,7 +354,7 @@ if there are still any actions you can take.

      -aliases = ['wait', 'hold']
      +aliases = ['hold', 'wait']
      @@ -375,7 +380,7 @@ if there are still any actions you can take.

      -search_index_entry = {'aliases': 'wait hold', 'category': 'combat', 'key': 'pass', 'tags': '', 'text': '\n Passes on your turn.\n\n Usage:\n pass\n\n When in a fight, you can use this command to end your turn early, even\n if there are still any actions you can take.\n '}
      +search_index_entry = {'aliases': 'hold wait', 'category': 'combat', 'key': 'pass', 'tags': '', 'text': '\n Passes on your turn.\n\n Usage:\n pass\n\n When in a fight, you can use this command to end your turn early, even\n if there are still any actions you can take.\n '}
      @@ -1045,7 +1050,7 @@ the fight. If all participants in a fight disengage, the fight ends.

      -_keyaliases = ('disengage', 'spare')
      +_keyaliases = ('spare', 'disengage')
      @@ -1264,7 +1269,7 @@ When it’s your turn, you can attack other characters.

      if there are still any actions you can take.

      -_keyaliases = ('wait', 'hold', 'pass')
      +_keyaliases = ('hold', 'wait', 'pass')
      @@ -1274,7 +1279,7 @@ if there are still any actions you can take.

      -aliases = ['wait', 'hold']
      +aliases = ['hold', 'wait']
      @@ -1300,7 +1305,7 @@ if there are still any actions you can take.

      -search_index_entry = {'aliases': 'wait hold', 'category': 'combat', 'key': 'pass', 'tags': '', 'text': '\n Passes on your turn.\n\n Usage:\n pass\n\n When in a fight, you can use this command to end your turn early, even\n if there are still any actions you can take.\n '}
      +search_index_entry = {'aliases': 'hold wait', 'category': 'combat', 'key': 'pass', 'tags': '', 'text': '\n Passes on your turn.\n\n Usage:\n pass\n\n When in a fight, you can use this command to end your turn early, even\n if there are still any actions you can take.\n '}
      @@ -2206,7 +2211,7 @@ the fight. If all participants in a fight disengage, the fight ends.

      -_keyaliases = ('disengage', 'spare')
      +_keyaliases = ('spare', 'disengage')
      @@ -2315,7 +2320,7 @@ When it’s your turn, you can attack other characters.

      if there are still any actions you can take.

      -_keyaliases = ('wait', 'hold', 'pass')
      +_keyaliases = ('hold', 'wait', 'pass')
      @@ -2325,7 +2330,7 @@ if there are still any actions you can take.

      -aliases = ['wait', 'hold']
      +aliases = ['hold', 'wait']
      @@ -2351,7 +2356,7 @@ if there are still any actions you can take.

      -search_index_entry = {'aliases': 'wait hold', 'category': 'combat', 'key': 'pass', 'tags': '', 'text': '\n Passes on your turn.\n\n Usage:\n pass\n\n When in a fight, you can use this command to end your turn early, even\n if there are still any actions you can take.\n '}
      +search_index_entry = {'aliases': 'hold wait', 'category': 'combat', 'key': 'pass', 'tags': '', 'text': '\n Passes on your turn.\n\n Usage:\n pass\n\n When in a fight, you can use this command to end your turn early, even\n if there are still any actions you can take.\n '}
      @@ -3369,7 +3374,7 @@ the fight. If all participants in a fight disengage, the fight ends.

      -_keyaliases = ('disengage', 'spare')
      +_keyaliases = ('spare', 'disengage')
      @@ -3546,7 +3551,7 @@ When it’s your turn, you can attack other characters.

      if there are still any actions you can take.

      -_keyaliases = ('wait', 'hold', 'pass')
      +_keyaliases = ('hold', 'wait', 'pass')
      @@ -3556,7 +3561,7 @@ if there are still any actions you can take.

      -aliases = ['wait', 'hold']
      +aliases = ['hold', 'wait']
      @@ -3582,7 +3587,7 @@ if there are still any actions you can take.

      -search_index_entry = {'aliases': 'wait hold', 'category': 'combat', 'key': 'pass', 'tags': '', 'text': '\n Passes on your turn.\n\n Usage:\n pass\n\n When in a fight, you can use this command to end your turn early, even\n if there are still any actions you can take.\n '}
      +search_index_entry = {'aliases': 'hold wait', 'category': 'combat', 'key': 'pass', 'tags': '', 'text': '\n Passes on your turn.\n\n Usage:\n pass\n\n When in a fight, you can use this command to end your turn early, even\n if there are still any actions you can take.\n '}
      @@ -4458,7 +4463,7 @@ the fight. If all participants in a fight disengage, the fight ends.

      -_keyaliases = ('disengage', 'spare')
      +_keyaliases = ('spare', 'disengage')
      @@ -4567,7 +4572,7 @@ When it’s your turn, you can attack other characters.

      if there are still any actions you can take.

      -_keyaliases = ('wait', 'hold', 'pass')
      +_keyaliases = ('hold', 'wait', 'pass')
      @@ -4577,7 +4582,7 @@ if there are still any actions you can take.

      -aliases = ['wait', 'hold']
      +aliases = ['hold', 'wait']
      @@ -4603,7 +4608,7 @@ if there are still any actions you can take.

      -search_index_entry = {'aliases': 'wait hold', 'category': 'combat', 'key': 'pass', 'tags': '', 'text': '\n Passes on your turn.\n\n Usage:\n pass\n\n When in a fight, you can use this command to end your turn early, even\n if there are still any actions you can take.\n '}
      +search_index_entry = {'aliases': 'hold wait', 'category': 'combat', 'key': 'pass', 'tags': '', 'text': '\n Passes on your turn.\n\n Usage:\n pass\n\n When in a fight, you can use this command to end your turn early, even\n if there are still any actions you can take.\n '}
      @@ -5574,7 +5579,10 @@ withdrawing - they can be considered to be moving to open space.

    51. modules |
    52. - + + + + @@ -218,7 +223,7 @@ the button is pushed.

      … not that there’s much to see in the dark.

      -_keyaliases = ('feel', 'ex', 'look', 'get', 'l', 'listen', 'examine')
      +_keyaliases = ('l', 'feel', 'get', 'examine', 'look', 'listen', 'ex')
      @@ -228,7 +233,7 @@ the button is pushed.

      -aliases = ['feel', 'ex', 'get', 'l', 'listen', 'examine']
      +aliases = ['l', 'feel', 'get', 'examine', 'listen', 'ex']
      @@ -259,7 +264,7 @@ the button is pushed.

      -search_index_entry = {'aliases': 'feel ex get l listen examine', 'category': 'general', 'key': 'look', 'tags': '', 'text': "\n Looking around in darkness\n\n Usage:\n look <obj>\n\n ... not that there's much to see in the dark.\n\n "}
      +search_index_entry = {'aliases': 'l feel get examine listen ex', 'category': 'general', 'key': 'look', 'tags': '', 'text': "\n Looking around in darkness\n\n Usage:\n look <obj>\n\n ... not that there's much to see in the dark.\n\n "}
      @@ -392,7 +397,7 @@ push the lid of the button away.

      -_keyaliases = ('open', 'open button', 'open lid')
      +_keyaliases = ('open button', 'open lid', 'open')
      @@ -402,7 +407,7 @@ push the lid of the button away.

      -aliases = ['open', 'open button']
      +aliases = ['open button', 'open']
      @@ -433,7 +438,7 @@ push the lid of the button away.

      -search_index_entry = {'aliases': 'open open button', 'category': 'general', 'key': 'open lid', 'tags': '', 'text': '\n open lid\n\n Usage:\n open lid\n\n '}
      +search_index_entry = {'aliases': 'open button open', 'category': 'general', 'key': 'open lid', 'tags': '', 'text': '\n open lid\n\n Usage:\n open lid\n\n '}
      @@ -449,7 +454,7 @@ push the lid of the button away.

      -_keyaliases = ('press button', 'push button', 'push', 'press')
      +_keyaliases = ('press button', 'press', 'push button', 'push')
      @@ -512,7 +517,7 @@ lid-state respectively.

      Try to smash the glass of the button.

      -_keyaliases = ('smash', 'smash glass', 'smash lid', 'break lid')
      +_keyaliases = ('smash glass', 'smash lid', 'smash', 'break lid')
      @@ -522,7 +527,7 @@ lid-state respectively.

      -aliases = ['smash', 'smash lid', 'break lid']
      +aliases = ['smash lid', 'smash', 'break lid']
      @@ -554,7 +559,7 @@ of causing the lamp to break.

      -search_index_entry = {'aliases': 'smash smash lid break lid', 'category': 'general', 'key': 'smash glass', 'tags': '', 'text': '\n smash glass\n\n Usage:\n smash glass\n\n Try to smash the glass of the button.\n '}
      +search_index_entry = {'aliases': 'smash lid smash break lid', 'category': 'general', 'key': 'smash glass', 'tags': '', 'text': '\n smash glass\n\n Usage:\n smash glass\n\n Try to smash the glass of the button.\n '}
      @@ -1240,7 +1245,10 @@ self.obj is the red_button on which this script is defined.

    53. modules |
    54. - + + + + @@ -61,7 +66,7 @@ building to activate the mob once it’s prepared.

      -_keyaliases = ('mobon', 'moboff')
      +_keyaliases = ('moboff', 'mobon')
      @@ -374,7 +379,7 @@ parry - forgoes your attack but will make you harder to hit on next

      -_keyaliases = ('chop', 'defend', 'attack', 'hit', 'thrust', 'pierce', 'kill', 'stab', 'fight', 'parry', 'slash')
      +_keyaliases = ('chop', 'pierce', 'parry', 'hit', 'slash', 'kill', 'stab', 'thrust', 'defend', 'fight', 'attack')
      @@ -384,7 +389,7 @@ parry - forgoes your attack but will make you harder to hit on next

      -aliases = ['defend', 'hit', 'thrust', 'pierce', 'kill', 'stab', 'parry', 'fight', 'chop', 'slash']
      +aliases = ['chop', 'pierce', 'parry', 'hit', 'defend', 'slash', 'kill', 'thrust', 'stab', 'fight']
      @@ -415,7 +420,7 @@ parry - forgoes your attack but will make you harder to hit on next

      -search_index_entry = {'aliases': 'defend hit thrust pierce kill stab parry fight chop slash', 'category': 'tutorialworld', 'key': 'attack', 'tags': '', 'text': '\n Attack the enemy. Commands:\n\n stab <enemy>\n slash <enemy>\n parry\n\n stab - (thrust) makes a lot of damage but is harder to hit with.\n slash - is easier to land, but does not make as much damage.\n parry - forgoes your attack but will make you harder to hit on next\n enemy attack.\n\n '}
      +search_index_entry = {'aliases': 'chop pierce parry hit defend slash kill thrust stab fight', 'category': 'tutorialworld', 'key': 'attack', 'tags': '', 'text': '\n Attack the enemy. Commands:\n\n stab <enemy>\n slash <enemy>\n parry\n\n stab - (thrust) makes a lot of damage but is harder to hit with.\n slash - is easier to land, but does not make as much damage.\n parry - forgoes your attack but will make you harder to hit on next\n enemy attack.\n\n '}
      @@ -543,7 +548,7 @@ itself handle all messages.

      Creates light where there was none. Something to burn.

      -_keyaliases = ('burn', 'light', 'on')
      +_keyaliases = ('light', 'on', 'burn')
      @@ -597,7 +602,7 @@ to sit on a “lightable” object, we operate only on self.obj.

      Presses a button.

      -_keyaliases = ('press', 'press button', 'push button', 'button')
      +_keyaliases = ('press button', 'press', 'push button', 'button')
      @@ -607,7 +612,7 @@ to sit on a “lightable” object, we operate only on self.obj.

      -aliases = ['press button', 'button', 'push button']
      +aliases = ['press button', 'push button', 'button']
      @@ -638,7 +643,7 @@ to sit on a “lightable” object, we operate only on self.obj.

      -search_index_entry = {'aliases': 'press button button push button', 'category': 'tutorialworld', 'key': 'press', 'tags': '', 'text': '\n Presses a button.\n '}
      +search_index_entry = {'aliases': 'press button push button button', 'category': 'tutorialworld', 'key': 'press', 'tags': '', 'text': '\n Presses a button.\n '}
      @@ -848,7 +853,7 @@ shift green root up/down

      -_keyaliases = ('pull', 'move', 'shift', 'shiftroot', 'push')
      +_keyaliases = ('move', 'shift', 'pull', 'push', 'shiftroot')
      @@ -858,7 +863,7 @@ shift green root up/down

      -aliases = ['pull', 'move', 'shiftroot', 'push']
      +aliases = ['shiftroot', 'push', 'move', 'pull']
      @@ -899,7 +904,7 @@ yellow/green - horizontal roots

      -search_index_entry = {'aliases': 'pull move shiftroot push', 'category': 'tutorialworld', 'key': 'shift', 'tags': '', 'text': '\n Shifts roots around.\n\n Usage:\n shift blue root left/right\n shift red root left/right\n shift yellow root up/down\n shift green root up/down\n\n '}
      +search_index_entry = {'aliases': 'shiftroot push move pull', 'category': 'tutorialworld', 'key': 'shift', 'tags': '', 'text': '\n Shifts roots around.\n\n Usage:\n shift blue root left/right\n shift red root left/right\n shift yellow root up/down\n shift green root up/down\n\n '}
      @@ -1739,7 +1744,7 @@ if they fall off the bridge.

      -_keyaliases = ('look', 'l')
      +_keyaliases = ('l', 'look')
      @@ -1798,7 +1803,7 @@ if they fall off the bridge.

      to find something.

      -_keyaliases = ('feel around', 'feel', 'fiddle', 'look', 'l', 'search')
      +_keyaliases = ('l', 'feel', 'feel around', 'search', 'look', 'fiddle')
      @@ -1808,7 +1813,7 @@ to find something.

      -aliases = ['feel around', 'feel', 'fiddle', 'l', 'search']
      +aliases = ['l', 'feel', 'feel around', 'search', 'fiddle']
      @@ -1841,7 +1846,7 @@ random chance of eventually finding a light source.

      -search_index_entry = {'aliases': 'feel around feel fiddle l search', 'category': 'tutorialworld', 'key': 'look', 'tags': '', 'text': '\n Look around in darkness\n\n Usage:\n look\n\n Look around in the darkness, trying\n to find something.\n '}
      +search_index_entry = {'aliases': 'l feel feel around search fiddle', 'category': 'tutorialworld', 'key': 'look', 'tags': '', 'text': '\n Look around in darkness\n\n Usage:\n look\n\n Look around in the darkness, trying\n to find something.\n '}
      @@ -1926,7 +1931,7 @@ actually having to be actual database objects. It uses the return_detail() hook on TutorialRooms for this.

      -_keyaliases = ('look', 'ls', 'l')
      +_keyaliases = ('l', 'ls', 'look')
      @@ -1936,7 +1941,7 @@ return_detail() hook on TutorialRooms for this.

      -aliases = ['ls', 'l']
      +aliases = ['l', 'ls']
      @@ -1963,7 +1968,7 @@ code except for adding in the details.

      -search_index_entry = {'aliases': 'ls l', 'category': 'tutorialworld', 'key': 'look', 'tags': '', 'text': '\n looks at the room and on details\n\n Usage:\n look <obj>\n look <room detail>\n look *<account>\n\n Observes your location, details at your location or objects\n in your vicinity.\n\n Tutorial: This is a child of the default Look command, that also\n allows us to look at "details" in the room. These details are\n things to examine and offers some extra description without\n actually having to be actual database objects. It uses the\n return_detail() hook on TutorialRooms for this.\n '}
      +search_index_entry = {'aliases': 'l ls', 'category': 'tutorialworld', 'key': 'look', 'tags': '', 'text': '\n looks at the room and on details\n\n Usage:\n look <obj>\n look <room detail>\n look *<account>\n\n Observes your location, details at your location or objects\n in your vicinity.\n\n Tutorial: This is a child of the default Look command, that also\n allows us to look at "details" in the room. These details are\n things to examine and offers some extra description without\n actually having to be actual database objects. It uses the\n return_detail() hook on TutorialRooms for this.\n '}
      @@ -2063,7 +2068,7 @@ on the bridge, 0 - 4.

      -_keyaliases = ('w', 'west')
      +_keyaliases = ('west', 'w')
      @@ -2633,7 +2638,10 @@ even though we don’t actually use them in this example)

    55. modules |
    56. - + + + + @@ -280,7 +285,10 @@ holds commands like creating a new account, logging in, etc.

    57. modules |
    58. - + + + + @@ -153,7 +158,10 @@ folder.

    59. modules |
    60. - + + + + @@ -544,7 +549,10 @@ web_root.putChild(“mypage”, my_page)

    61. modules |
    62. - + + + + @@ -117,7 +122,10 @@
    63. modules |
    64. - + + + + @@ -122,7 +127,10 @@
    65. modules |
    66. - + + + + @@ -101,7 +106,10 @@ help.

    67. modules |
    68. - + + + + @@ -128,7 +133,10 @@ any other keywords are interpreted as Attributes and their values.

    69. modules |
    70. - + + + + @@ -735,7 +740,10 @@ responsibility.

    71. modules |
    72. - + + + + @@ -634,7 +639,10 @@ always be sure of what you have changed and what is default behaviour.

    73. modules |
    74. - + + + + @@ -1129,7 +1134,10 @@ the stability and integrity of the codebase during updates.

    75. modules |
    76. - + + + + @@ -3073,7 +3078,10 @@ in the the core sessionhandler.

    77. modules |
    78. - + + + + @@ -2066,7 +2071,10 @@ custom prototype_parents are given to this function.

    79. modules |
    80. - + + + + @@ -1838,7 +1843,10 @@ interval.

    81. modules |
    82. - + + + + @@ -257,7 +262,10 @@ Stop the service so we’re not wasting resources.

    83. modules |
    84. - + + + + @@ -3348,7 +3353,10 @@ so as to register correctly with the twisted daemon.

    85. modules |
    86. - + + + + @@ -3862,7 +3867,10 @@ implement default like this:

    87. modules |
    88. - + + + + @@ -615,7 +620,10 @@ This message will get attached time stamp.

    89. modules |
    90. - + + + + @@ -447,7 +452,10 @@ If not given, a subset of settings.INSTALLED_APPS will be used.

    91. modules |
    92. - + + + + @@ -3883,7 +3888,10 @@ as .clear().

    93. modules |
    94. - + + + + @@ -1902,7 +1907,7 @@ txt - extra text (string), could be encased in quotes.

      Commands for the editor

      -_keyaliases = (':dd', ':I', ':S', ':A', ':dw', ':DD', ':wq', ':uu', ':echo', ':y', ':q!', ':x', ':', ':fi', '::', ':i', ':h', ':<', ':!', ':>', ':::', ':=', ':editor_command_group', ':w', ':j', ':q', ':UU', ':r', ':fd', ':s', ':u', ':f', ':p')
      +_keyaliases = (':', ':echo', ':>', ':UU', ':r', ':h', ':dw', ':u', ':s', ':w', ':p', ':S', ':!', ':x', ':I', ':DD', ':dd', ':j', '::', ':i', ':q', ':editor_command_group', ':A', ':f', ':wq', ':=', ':fi', ':y', ':::', ':<', ':q!', ':fd', ':uu')
      @@ -1912,7 +1917,7 @@ txt - extra text (string), could be encased in quotes.

      -aliases = [':dd', ':I', ':S', ':A', ':dw', ':DD', ':wq', ':uu', ':echo', ':y', ':q!', ':x', ':', ':fi', '::', ':i', ':h', ':<', ':!', ':>', ':::', ':=', ':w', ':j', ':q', ':UU', ':r', ':fd', ':s', ':u', ':f', ':p']
      +aliases = [':', ':echo', ':>', ':UU', ':r', ':h', ':dw', ':u', ':s', ':w', ':p', ':S', ':!', ':uu', ':x', ':I', ':DD', ':dd', ':j', '::', ':i', ':q', ':A', ':f', ':wq', ':=', ':fi', ':fd', ':<', ':q!', ':::', ':y']
      @@ -1945,7 +1950,7 @@ efficient presentation.

      -search_index_entry = {'aliases': ':dd :I :S :A :dw :DD :wq :uu :echo :y :q! :x : :fi :: :i :h :< :! :> ::: := :w :j :q :UU :r :fd :s :u :f :p', 'category': 'general', 'key': ':editor_command_group', 'tags': '', 'text': '\n Commands for the editor\n '}
      +search_index_entry = {'aliases': ': :echo :> :UU :r :h :dw :u :s :w :p :S :! :uu :x :I :DD :dd :j :: :i :q :A :f :wq := :fi :fd :< :q! ::: :y', 'category': 'general', 'key': ':editor_command_group', 'tags': '', 'text': '\n Commands for the editor\n '}
      @@ -1957,7 +1962,7 @@ efficient presentation.

      No command match - Inputs line of text into buffer.

      -_keyaliases = ('__nomatch_command', '__noinput_command')
      +_keyaliases = ('__noinput_command', '__nomatch_command')
      @@ -2009,7 +2014,7 @@ nomatches (defaults to Yes), and avoid saves only if command was given specifically as “no” or “n”.

      -_keyaliases = ('__nomatch_command', '__noinput_command')
      +_keyaliases = ('__noinput_command', '__nomatch_command')
      @@ -2724,7 +2729,7 @@ evennia.utils.evmenu.

      Menu options.

      -_keyaliases = ('__nomatch_command', '__noinput_command')
      +_keyaliases = ('__noinput_command', '__nomatch_command')
      @@ -2777,7 +2782,7 @@ evennia.utils.evmenu.

      Enter your data and press return.

      -_keyaliases = ('__nomatch_command', '__noinput_command')
      +_keyaliases = ('__noinput_command', '__nomatch_command')
      @@ -3574,7 +3579,7 @@ caller.msg() construct every time the page is updated.

      Manipulate the text paging

      -_keyaliases = ('e', 'abort', 'q', 'next', 'n', 'b', 'end', 'back', 'top', 't', '__noinput_command', 'quit', 'a')
      +_keyaliases = ('e', '__noinput_command', 'q', 'n', 't', 'back', 'top', 'next', 'end', 'abort', 'b', 'quit', 'a')
      @@ -3584,7 +3589,7 @@ caller.msg() construct every time the page is updated.

      -aliases = ['e', 'abort', 'q', 'next', 'n', 'b', 'end', 'back', 'top', 't', 'quit', 'a']
      +aliases = ['e', 'q', 'n', 't', 'back', 'top', 'next', 'end', 'abort', 'b', 'quit', 'a']
      @@ -3615,7 +3620,7 @@ caller.msg() construct every time the page is updated.

      -search_index_entry = {'aliases': 'e abort q next n b end back top t quit a', 'category': 'general', 'key': '__noinput_command', 'tags': '', 'text': '\n Manipulate the text paging\n '}
      +search_index_entry = {'aliases': 'e q n t back top next end abort b quit a', 'category': 'general', 'key': '__noinput_command', 'tags': '', 'text': '\n Manipulate the text paging\n '}
      @@ -3627,7 +3632,7 @@ caller.msg() construct every time the page is updated.

      Override look to display window and prevent OOCLook from firing

      -_keyaliases = ('look', 'l')
      +_keyaliases = ('l', 'look')
      @@ -9586,7 +9591,10 @@ If neither one is provided, defaults to UTC.

    95. modules |
    96. - + + + + @@ -776,7 +781,10 @@ class built by crea
    97. modules |
    98. - + + + + @@ -108,7 +113,10 @@ prototype dictionaries)

    99. modules |
    100. - + + + + @@ -1270,7 +1275,10 @@ time_format(seconds, style=0)

    101. modules |
    102. - + + + + @@ -140,7 +145,10 @@ found under the server package).

    103. modules |
    104. - + + + + @@ -205,7 +210,10 @@ to webclient settings.

    105. modules |
    106. - + + + + @@ -112,7 +117,10 @@ page and serve it eventual static content.

    107. modules |
    108. - + + + + @@ -1585,7 +1590,10 @@ implemented yet.

    109. modules |
    110. - + + + + @@ -103,7 +108,10 @@
    111. modules |
    112. - + + + + @@ -16719,7 +16724,10 @@
    113. modules |
    114. - + + + + @@ -118,7 +121,8 @@ suggestion box!

    115. modules |
    116. - + + @@ -1449,7 +1454,10 @@
    117. modules |
    118. - + + + + @@ -90,7 +95,10 @@
    119. modules |
    120. - + + + + @@ -252,7 +257,10 @@
    121. modules |
    122. - + + + + @@ -39,12 +44,23 @@ -

      This tutorial will walk you through the steps to create a voice-operated elevator, using the in-game Python system. This tutorial assumes the in-game Python system is installed in your game. If it isn’t, you can follow the installation steps given in the documentation on in-game Python, and come back on this tutorial once the system is installed. You do not need to read the entire documentation, it’s a good reference, but not the easiest way to learn about it. Hence these tutorials.

      -

      The in-game Python system allows to run code on individual objects in some situations. You don’t have to modify the source code to add these features, past the installation. The entire system makes it easy to add specific features to some objects, but not all.

      +

      This tutorial will walk you through the steps to create a voice-operated elevator, using the in- +game Python +system. +This tutorial assumes the in-game Python system is installed in your game. If it isn’t, you can +follow the installation steps given in the documentation on in-game +Python, and +come back on this tutorial once the system is installed. You do not need to read the entire +documentation, it’s a good reference, but not the easiest way to learn about it. Hence these +tutorials.

      +

      The in-game Python system allows to run code on individual objects in some situations. You don’t +have to modify the source code to add these features, past the installation. The entire system +makes it easy to add specific features to some objects, but not all.

      What will we try to do?

      -

      In this tutorial, we are going to create a simple voice-operated elevator. In terms of features, we will:

      +

      In this tutorial, we are going to create a simple voice-operated elevator. In terms of features, we +will:

      • Explore events with parameters.

      • Work on more interesting callbacks.

      • @@ -53,16 +69,26 @@

      Our study case

      -

      Let’s summarize what we want to achieve first. We would like to create a room that will represent the inside of our elevator. In this room, a character could just say “1”, “2” or “3”, and the elevator will start moving. The doors will close and open on the new floor (the exits leading in and out of the elevator will be modified).

      -

      We will work on basic features first, and then will adjust some, showing you how easy and powerfully independent actions can be configured through the in-game Python system.

      +

      Let’s summarize what we want to achieve first. We would like to create a room that will represent +the inside of our elevator. In this room, a character could just say “1”, “2” or “3”, and the +elevator will start moving. The doors will close and open on the new floor (the exits leading in +and out of the elevator will be modified).

      +

      We will work on basic features first, and then will adjust some, showing you how easy and powerfully +independent actions can be configured through the in-game Python system.

      Creating the rooms and exits we need

      -

      We’ll create an elevator right in our room (generally called “Limbo”, of ID 2). You could easily adapt the following instructions if you already have some rooms and exits, of course, just remember to check the IDs.

      +

      We’ll create an elevator right in our room (generally called “Limbo”, of ID 2). You could easily +adapt the following instructions if you already have some rooms and exits, of course, just remember +to check the IDs.

      -

      Note: the in-game Python system uses IDs for a lot of things. While it is not mandatory, it is good practice to know the IDs you have for your callbacks, because it will make manipulation much quicker. There are other ways to identify objects, but as they depend on many factors, IDs are usually the safest path in our callbacks.

      +

      Note: the in-game Python system uses IDs for a lot of things. While it is not mandatory, it is +good practice to know the IDs you have for your callbacks, because it will make manipulation much +quicker. There are other ways to identify objects, but as they depend on many factors, IDs are +usually the safest path in our callbacks.

      -

      Let’s go into limbo (#2) to add our elevator. We’ll add it to the north. To create this room, in-game you could type:

      +

      Let’s go into limbo (#2) to add our elevator. We’ll add it to the north. To create this room, +in-game you could type:

      tunnel n = Inside of an elevator
       
      @@ -83,8 +109,11 @@

      Why have we created exits to our elevator and back to Limbo? Isn’t the elevator supposed to move?

      -

      It is. But we need to have exits that will represent the way inside the elevator and out. What we will do, at every floor, will be to change these exits so they become connected to the right room. You’ll see this process a bit later.

      -

      We have two more rooms to create: our floor 2 and 3. This time, we’ll use dig, because we don’t need exits leading there, not yet anyway.

      +

      It is. But we need to have exits that will represent the way inside the elevator and out. What we +will do, at every floor, will be to change these exits so they become connected to the right room. +You’ll see this process a bit later.

      +

      We have two more rooms to create: our floor 2 and 3. This time, we’ll use dig, because we don’t +need exits leading there, not yet anyway.

      dig The second floor
       dig The third floor
       
      @@ -99,21 +128,36 @@

      Our first callback in the elevator

      Let’s go to the elevator (you could use tel #3 if you have the same IDs I have).

      -

      This is our elevator room. It looks a bit empty, feel free to add a prettier description or other things to decorate it a bit.

      -

      But what we want now is to be able to say “1”, “2” or “3” and have the elevator move in that direction.

      -

      If you have read the previous tutorial about adding dialogues in events, you may remember what we need to do. If not, here’s a summary: we need to run some code when somebody speaks in the room. So we need to create a callback (the callback will contain our lines of code). We just need to know on which event this should be set. You can enter call here to see the possible events in this room.

      -

      In the table, you should see the “say” event, which is called when somebody says something in the room. So we’ll need to add a callback to this event. Don’t worry if you’re a bit lost, just follow the following steps, the way they connect together will become more obvious.

      +

      This is our elevator room. It looks a bit empty, feel free to add a prettier description or other +things to decorate it a bit.

      +

      But what we want now is to be able to say “1”, “2” or “3” and have the elevator move in that +direction.

      +

      If you have read the previous tutorial about adding dialogues in events, you +may remember what we need to do. If not, here’s a summary: we need to run some code when somebody +speaks in the room. So we need to create a callback (the callback will contain our lines of code). +We just need to know on which event this should be set. You can enter call here to see the +possible events in this room.

      +

      In the table, you should see the “say” event, which is called when somebody says something in the +room. So we’ll need to add a callback to this event. Don’t worry if you’re a bit lost, just follow +the following steps, the way they connect together will become more obvious.

      call/add here = say 1, 2, 3
       
        -
      1. We need to add a callback. A callback contains the code that will be executed at a given time. So we use the call/add command and switch.

      2. +
      3. We need to add a callback. A callback contains the code that will be executed at a given time. +So we use the call/add command and switch.

      4. here is our object, the room in which we are.

      5. An equal sign.

      6. -
      7. The name of the event to which the callback should be connected. Here, the event is “say”. Meaning this callback will be executed every time somebody says something in the room.

      8. -
      9. But we add an event parameter to indicate the keywords said in the room that should execute our callback. Otherwise, our callback would be called every time somebody speaks, no matter what. Here we limit, indicating our callback should be executed only if the spoken message contains “1”, “2” or “3”.

      10. +
      11. The name of the event to which the callback should be connected. Here, the event is “say”. +Meaning this callback will be executed every time somebody says something in the room.

      12. +
      13. But we add an event parameter to indicate the keywords said in the room that should execute our +callback. Otherwise, our callback would be called every time somebody speaks, no matter what. Here +we limit, indicating our callback should be executed only if the spoken message contains “1”, “2” or +“3”.

      -

      An editor should open, inviting you to enter the Python code that should be executed. The first thing to remember is to read the text provided (it can contain important information) and, most of all, the list of variables that are available in this callback:

      +

      An editor should open, inviting you to enter the Python code that should be executed. The first +thing to remember is to read the text provided (it can contain important information) and, most of +all, the list of variables that are available in this callback:

      Variables you can use in this event:
       
           character: the character having spoken in this room.
      @@ -125,22 +169,27 @@
       ----------[l:01 w:000 c:0000]------------(:h for help)----------------------------
       
      -

      This is important, in order to know what variables we can use in our callback out-of-the-box. Let’s write a single line to be sure our callback is called when we expect it to:

      +

      This is important, in order to know what variables we can use in our callback out-of-the-box. Let’s +write a single line to be sure our callback is called when we expect it to:

      1
      character.msg("You just said {}.".format(message))
       
      -

      You can paste this line in-game, then type the :wq command to exit the editor and save your modifications.

      -

      Let’s check. Try to say “hello” in the room. You should see the standard message, but nothing more. Now try to say “1”. Below the standard message, you should see:

      +

      You can paste this line in-game, then type the :wq command to exit the editor and save your +modifications.

      +

      Let’s check. Try to say “hello” in the room. You should see the standard message, but nothing +more. Now try to say “1”. Below the standard message, you should see:

      You just said 1.
       
      -

      You can try it. Our callback is only called when we say “1”, “2” or “3”. Which is just what we want.

      +

      You can try it. Our callback is only called when we say “1”, “2” or “3”. Which is just what we +want.

      Let’s go back in our code editor and add something more useful.

      call/edit here = say
       
      -

      Notice that we used the “edit” switch this time, since the callback exists, we just want to edit it.

      +

      Notice that we used the “edit” switch this time, since the callback exists, we just want to edit +it.

      The editor opens again. Let’s empty it first:

      :DD
      @@ -151,7 +200,8 @@
       
      -

      Auto-indentation is an interesting feature of the code editor, but we’d better not use it at this point, it will make copy/pasting more complicated.

      +

      Auto-indentation is an interesting feature of the code editor, but we’d better not use it at this +point, it will make copy/pasting more complicated.

      @@ -159,9 +209,11 @@

      So here’s the time to truly code our callback in-game. Here’s a little reminder:

      1. We have all the IDs of our three rooms and two exits.

      2. -
      3. When we say “1”, “2” or “3”, the elevator should move to the right room, that is change the exits. Remember, we already have the exits, we just need to change their location and destination.

      4. +
      5. When we say “1”, “2” or “3”, the elevator should move to the right room, that is change the +exits. Remember, we already have the exits, we just need to change their location and destination.

      -

      It’s a good idea to try to write this callback yourself, but don’t feel bad about checking the solution right now. Here’s a possible code that you could paste in the code editor:

      +

      It’s a good idea to try to write this callback yourself, but don’t feel bad about checking the +solution right now. Here’s a possible code that you could paste in the code editor:

       1
        2
        3
      @@ -211,33 +263,53 @@
       

      Let’s review this longer callback:

        -
      1. We first obtain the objects of both exits and our three floors. We use the get() eventfunc, which is a shortcut to obtaining objects. We usually use it to retrieve specific objects with an ID. We put the floors in a dictionary. The keys of the dictionary are the floor number (as str), the values are room objects.

      2. -
      3. Remember, the message variable contains the message spoken in the room. So either “1”, “2”, or “3”. We still need to check it, however, because if the character says something like “1 2” in the room, our callback will be executed. Let’s be sure what she says is a floor number.

      4. -
      5. We then check if the elevator is already at this floor. Notice that we use TO_EXIT.location. TO_EXIT contains our “north” exit, leading inside of our elevator. Therefore, its location will be the room where the elevator currently is.

      6. -
      7. If the floor is a different one, have the elevator “move”, changing just the location and destination of both exits.

        +
      8. We first obtain the objects of both exits and our three floors. We use the get() eventfunc, +which is a shortcut to obtaining objects. We usually use it to retrieve specific objects with an +ID. We put the floors in a dictionary. The keys of the dictionary are the floor number (as str), +the values are room objects.

      9. +
      10. Remember, the message variable contains the message spoken in the room. So either “1”, “2”, or +“3”. We still need to check it, however, because if the character says something like “1 2” in the +room, our callback will be executed. Let’s be sure what she says is a floor number.

      11. +
      12. We then check if the elevator is already at this floor. Notice that we use TO_EXIT.location. +TO_EXIT contains our “north” exit, leading inside of our elevator. Therefore, its location will +be the room where the elevator currently is.

      13. +
      14. If the floor is a different one, have the elevator “move”, changing just the location and +destination of both exits.

          -
        • The BACK_EXIT (that is “north”) should change its location. The elevator shouldn’t be accessible through our old floor.

        • -
        • The TO_EXIT (that is “south”, the exit leading out of the elevator) should have a different destination. When we go out of the elevator, we should find ourselves in the new floor, not the old one.

        • +
        • The BACK_EXIT (that is “north”) should change its location. The elevator shouldn’t be +accessible through our old floor.

        • +
        • The TO_EXIT (that is “south”, the exit leading out of the elevator) should have a different +destination. When we go out of the elevator, we should find ourselves in the new floor, not the old +one.

      -

      Feel free to expand on this example, changing messages, making further checks. Usage and practice are keys.

      +

      Feel free to expand on this example, changing messages, making further checks. Usage and practice +are keys.

      You can quit the editor as usual with :wq and test it out.

      Adding a pause in our callback

      -

      Let’s improve our callback. One thing that’s worth adding would be a pause: for the time being, when we say the floor number in the elevator, the doors close and open right away. It would be better to have a pause of several seconds. More logical.

      -

      This is a great opportunity to learn about chained events. Chained events are very useful to create pauses. Contrary to the events we have seen so far, chained events aren’t called automatically. They must be called by you, and can be called after some time.

      +

      Let’s improve our callback. One thing that’s worth adding would be a pause: for the time being, +when we say the floor number in the elevator, the doors close and open right away. It would be +better to have a pause of several seconds. More logical.

      +

      This is a great opportunity to learn about chained events. Chained events are very useful to create +pauses. Contrary to the events we have seen so far, chained events aren’t called automatically. +They must be called by you, and can be called after some time.

        -
      • Chained events always have the name “chain_X”. Usually, X is a number, but you can give the chained event a more explicit name.

      • +
      • Chained events always have the name “chain_X”. Usually, X is a number, but you can give the +chained event a more explicit name.

      • In our original callback, we will call our chained events in, say, 15 seconds.

      • We’ll also have to make sure the elevator isn’t already moving.

      -

      Other than that, a chained event can be connected to a callback as usual. We’ll create a chained event in our elevator, that will only contain the code necessary to open the doors to the new floor.

      +

      Other than that, a chained event can be connected to a callback as usual. We’ll create a chained +event in our elevator, that will only contain the code necessary to open the doors to the new floor.

      call/add here = chain_1
       
      -

      The callback is added to the “chain_1” event, an event that will not be automatically called by the system when something happens. Inside this event, you can paste the code to open the doors at the new floor. You can notice a few differences:

      +

      The callback is added to the “chain_1” event, an event that will not be automatically called by the +system when something happens. Inside this event, you can paste the code to open the doors at the +new floor. You can notice a few differences:

      1
       2
       3
      @@ -319,20 +391,37 @@
       

      What changed?

        -
      1. We added a little test to make sure the elevator wasn’t already moving. If it is, the BACK_EXIT.location (the “south” exit leading out of the elevator) should be None. We’ll remove the exit while the elevator is moving.

      2. -
      3. When the doors close, we set both exits’ location to None. Which “removes” them from their room but doesn’t destroy them. The exits still exist but they don’t connect anything. If you say “2” in the elevator and look around while the elevator is moving, you won’t see any exits.

      4. -
      5. Instead of opening the doors immediately, we call call_event. We give it the object containing the event to be called (here, our elevator), the name of the event to be called (here, “chain_1”) and the number of seconds from now when the event should be called (here, 15).

      6. -
      7. The chain_1 callback we have created contains the code to “re-open” the elevator doors. That is, besides displaying a message, it reset the exits’ location and destination.

      8. +
      9. We added a little test to make sure the elevator wasn’t already moving. If it is, the +BACK_EXIT.location (the “south” exit leading out of the elevator) should be None. We’ll remove +the exit while the elevator is moving.

      10. +
      11. When the doors close, we set both exits’ location to None. Which “removes” them from their +room but doesn’t destroy them. The exits still exist but they don’t connect anything. If you say +“2” in the elevator and look around while the elevator is moving, you won’t see any exits.

      12. +
      13. Instead of opening the doors immediately, we call call_event. We give it the object containing +the event to be called (here, our elevator), the name of the event to be called (here, “chain_1”) +and the number of seconds from now when the event should be called (here, 15).

      14. +
      15. The chain_1 callback we have created contains the code to “re-open” the elevator doors. That +is, besides displaying a message, it reset the exits’ location and destination.

      -

      If you try to say “3” in the elevator, you should see the doors closing. Look around you and you won’t see any exit. Then, 15 seconds later, the doors should open, and you can leave the elevator to go to the third floor. While the elevator is moving, the exit leading to it will be inaccessible.

      +

      If you try to say “3” in the elevator, you should see the doors closing. Look around you and you +won’t see any exit. Then, 15 seconds later, the doors should open, and you can leave the elevator +to go to the third floor. While the elevator is moving, the exit leading to it will be +inaccessible.

      -

      Note: we don’t define the variables again in our chained event, we just call them. When we execute call_event, a copy of our current variables is placed in the database. These variables will be restored and accessible again when the chained event is called.

      +

      Note: we don’t define the variables again in our chained event, we just call them. When we +execute call_event, a copy of our current variables is placed in the database. These variables +will be restored and accessible again when the chained event is called.

      -

      You can use the call/tasks command to see the tasks waiting to be executed. For instance, say “2” in the room, notice the doors closing, and then type the call/tasks command. You will see a task in the elevator, waiting to call the chain_1 event.

      +

      You can use the call/tasks command to see the tasks waiting to be executed. For instance, say “2” +in the room, notice the doors closing, and then type the call/tasks command. You will see a task +in the elevator, waiting to call the chain_1 event.

      Changing exit messages

      -

      Here’s another nice little feature of events: you can modify the message of a single exit without altering the others. In this case, when someone goes north into our elevator, we’d like to see something like: “someone walks into the elevator.” Something similar for the back exit would be great too.

      +

      Here’s another nice little feature of events: you can modify the message of a single exit without +altering the others. In this case, when someone goes north into our elevator, we’d like to see +something like: “someone walks into the elevator.” Something similar for the back exit would be +great too.

      Inside of the elevator, you can look at the available events on the exit leading outside (south).

      call south
       
      @@ -344,11 +433,14 @@ | | | through this exit. |
      -

      So we can change the message others see when a character leaves, by editing the “msg_leave” event. Let’s do that:

      +

      So we can change the message others see when a character leaves, by editing the “msg_leave” event. +Let’s do that:

      call/add south = msg_leave
       
      -

      Take the time to read the help. It gives you all the information you should need. We’ll need to change the “message” variable, and use custom mapping (between braces) to alter the message. We’re given an example, let’s use it. In the code editor, you can paste the following line:

      +

      Take the time to read the help. It gives you all the information you should need. We’ll need to +change the “message” variable, and use custom mapping (between braces) to alter the message. We’re +given an example, let’s use it. In the code editor, you can paste the following line:

      1
      message = "{character} walks out of the elevator."
       
      @@ -361,34 +453,56 @@
      py self.search("beggar").move_to(self.search("south"))
       
      -

      This is a crude way to force our beggar out of the elevator, but it allows us to test. You should see:

      +

      This is a crude way to force our beggar out of the elevator, but it allows us to test. You should +see:

      A beggar(#8) walks out of the elevator.
       
      -

      Great! Let’s do the same thing for the exit leading inside of the elevator. Follow the beggar, then edit “msg_leave” of “north”:

      +

      Great! Let’s do the same thing for the exit leading inside of the elevator. Follow the beggar, +then edit “msg_leave” of “north”:

      call/add north = msg_leave
       
      1
      message = "{character} walks into the elevator."
       
      -

      Again, you can force our beggar to move and see the message we have just set. This modification applies to these two exits, obviously: the custom message won’t be used for other exits. Since we use the same exits for every floor, this will be available no matter at what floor the elevator is, which is pretty neat!

      +

      Again, you can force our beggar to move and see the message we have just set. This modification +applies to these two exits, obviously: the custom message won’t be used for other exits. Since we +use the same exits for every floor, this will be available no matter at what floor the elevator is, +which is pretty neat!

      Tutorial F.A.Q.

      • Q: what happens if the game reloads or shuts down while a task is waiting to happen?

      • -
      • A: if your game reloads while a task is in pause (like our elevator between floors), when the game is accessible again, the task will be called (if necessary, with a new time difference to take into account the reload). If the server shuts down, obviously, the task will not be called, but will be stored and executed when the server is up again.

      • +
      • A: if your game reloads while a task is in pause (like our elevator between floors), when the +game is accessible again, the task will be called (if necessary, with a new time difference to take +into account the reload). If the server shuts down, obviously, the task will not be called, but +will be stored and executed when the server is up again.

      • Q: can I use all kinds of variables in my callback? Whether chained or not?

      • -
      • A: you can use every variable type you like in your original callback. However, if you execute call_event, since your variables are stored in the database, they will need to respect the constraints on persistent attributes. A callback will not be stored in this way, for instance. This variable will not be available in your chained event.

      • -
      • Q: when you say I can call my chained events something else than “chain_1”, “chain_2” and such, what is the naming convention?

      • -
      • A: chained events have names beginning by “chain_”. This is useful for you and for the system. But after the underscore, you can give a more useful name, like “chain_open_doors” in our case.

      • +
      • A: you can use every variable type you like in your original callback. However, if you +execute call_event, since your variables are stored in the database, they will need to respect the +constraints on persistent attributes. A callback will not be stored in this way, for instance. +This variable will not be available in your chained event.

      • +
      • Q: when you say I can call my chained events something else than “chain_1”, “chain_2” and +such, what is the naming convention?

      • +
      • A: chained events have names beginning by “chain_”. This is useful for you and for the +system. But after the underscore, you can give a more useful name, like “chain_open_doors” in our +case.

      • Q: do I have to pause several seconds to call a chained event?

      • -
      • A: no, you can call it right away. Just leave the third parameter of call_event out (it will default to 0, meaning the chained event will be called right away). This will not create a task.

      • +
      • A: no, you can call it right away. Just leave the third parameter of call_event out (it +will default to 0, meaning the chained event will be called right away). This will not create a +task.

      • Q: can I have chained events calling themselves?

      • -
      • A: you can. There’s no limitation. Just be careful, a callback that calls itself, particularly without delay, might be a good recipe for an infinite loop. However, in some cases, it is useful to have chained events calling themselves, to do the same repeated action every X seconds for instance.

      • +
      • A: you can. There’s no limitation. Just be careful, a callback that calls itself, +particularly without delay, might be a good recipe for an infinite loop. However, in some cases, it +is useful to have chained events calling themselves, to do the same repeated action every X seconds +for instance.

      • Q: what if I need several elevators, do I need to copy/paste these callbacks each time?

      • -
      • A: not advisable. There are definitely better ways to handle this situation. One of them is to consider adding the code in the source itself. Another possibility is to call chained events with the expected behavior, which makes porting code very easy. This side of chained events will be shown in the next tutorial.

      • +
      • A: not advisable. There are definitely better ways to handle this situation. One of them is +to consider adding the code in the source itself. Another possibility is to call chained events +with the expected behavior, which makes porting code very easy. This side of chained events will be +shown in the next tutorial.

      • Previous tutorial: Adding dialogues in events

      @@ -453,7 +567,10 @@
    123. modules |
    124. - + + + +
      @@ -36,17 +41,30 @@

      API refactoring

      -

      Building up to Evennia 1.0 and beyond, it’s time to comb through the Evennia API for old cruft. This whitepage is for anyone interested to contribute with their views on what part of the API needs refactoring, cleanup or clarification (or extension!)

      -

      Note that this is not a forum. To keep things clean, each opinion text should ideally present a clear argument or lay out a suggestion. Asking for clarification and any side-discussions should be held in chat or forum.

      +

      Building up to Evennia 1.0 and beyond, it’s time to comb through the Evennia API for old cruft. This +whitepage is for anyone interested to contribute with their views on what part of the API needs +refactoring, cleanup or clarification (or extension!)

      +

      Note that this is not a forum. To keep things clean, each opinion text should ideally present a +clear argument or lay out a suggestion. Asking for clarification and any side-discussions should be +held in chat or forum.


      Griatch (Aug 13, 2019)

      -

      This is how to enter an opinion. Use any markdown needed but stay within your section. Also remember to copy your text to the clipboard before saving since if someone else edited the wiki in the meantime you’ll have to start over.

      +

      This is how to enter an opinion. Use any markdown needed but stay within your section. Also remember +to copy your text to the clipboard before saving since if someone else edited the wiki in the +meantime you’ll have to start over.

      Griatch (Sept 2, 2019)

      -

      I don’t agree with removing explicit keywords as suggested by Johnny on Aug 29 below. Overriding such a method can still be done by get(self, **kwargs) if so desired, making the kwargs explicit helps IMO readability of the API. If just giving a generic **kwargs, one must read the docstring or even the code to see which keywords are valid.

      -

      On the other hand, I think it makes sense to as a standard offer an extra **kwargs at the end of arg-lists for common methods that are expected to be over-ridden. This make the API more flexible by hinting to the dev that they could expand their own over-ridden implementation with their own keyword arguments if so desired.

      +

      I don’t agree with removing explicit keywords as suggested by [Johnny on Aug 29 below](API- +refactoring#reduce-usage-of-optionalpositional-arguments-aug-29-2019). Overriding such a method can +still be done by get(self, **kwargs) if so desired, making the kwargs explicit helps IMO +readability of the API. If just giving a generic **kwargs, one must read the docstring or even the +code to see which keywords are valid.

      +

      On the other hand, I think it makes sense to as a standard offer an extra **kwargs at the end of +arg-lists for common methods that are expected to be over-ridden. This make the API more flexible by +hinting to the dev that they could expand their own over-ridden implementation with their own +keyword arguments if so desired.


      @@ -59,7 +77,10 @@ default_access=True, return_list=False):
      -

      Many classes have methods requiring lengthy positional argument lists, which are tedious and error-prone to extend and override especially in cases where not all arguments are even required. It would be useful if arguments were reserved for required inputs and anything else relegated to kwargs for easier passthrough on extension.

      +

      Many classes have methods requiring lengthy positional argument lists, which are tedious and error- +prone to extend and override especially in cases where not all arguments are even required. It would +be useful if arguments were reserved for required inputs and anything else relegated to kwargs for +easier passthrough on extension.

      @@ -122,7 +143,10 @@
    125. modules |
    126. - + + + + @@ -39,9 +44,9 @@

      All users (real people) that starts a game Session on Evennia are doing so through an object called Account. The Account object has no in-game representation, it represents a unique game account. In order to actually get on the game the Account must puppet an Object -(normally a Character).

      +(normally a Character).

      Exactly how many Sessions can interact with an Account and its Puppets at once is determined by -Evennia’s MULTISESSION_MODE setting.

      +Evennia’s MULTISESSION_MODE setting.

      Apart from storing login information and other account-specific data, the Account object is what is chatting on Channels. It is also a good place to store Permissions to be consistent between different in-game characters as well as configuration options. The Account @@ -52,7 +57,7 @@ like a simple chat program. It acts as a staging area for switching between Cha game supports that) or as a safety mode if your Character gets deleted. Use ic to attempt to (re)puppet a Character.

      Note that the Account object can have, and often does have, a different set of -Permissions from the Character they control. Normally you should put your +Permissions from the Character they control. Normally you should put your permissions on the Account level - this will overrule permissions set on the Character level. For the permissions of the Character to come into play the default quell command can be used. This allows for exploring the game using a different permission set (but you can’t escalate your @@ -117,7 +122,8 @@ Account also has the following custom properties:

    127. sessions - an instance of ObjectSessionHandler managing all connected Sessions (physical connections) this object listens to (Note: In older -versions of Evennia, this was a list). The so-called session-id (used in many places) is found as +versions of Evennia, this was a list). The so-called session-id (used in many places) is found +as a property sessid on each Session instance.

    128. is_superuser (bool: True/False) - if this account is a superuser.

    129. @@ -126,7 +132,8 @@ a property sessid

      cmdset - This holds all the current Commands of this Account. By default these are the commands found in the cmdset defined by settings.CMDSET_ACCOUNT.

    130. nicks - This stores and handles Nicks, in the same way as nicks it works on Objects. -For Accounts, nicks are primarily used to store custom aliases for Channels.

    131. +For Accounts, nicks are primarily used to store custom aliases for +Channels.

      Selection of special methods (see evennia.DefaultAccount for details):

      @@ -204,7 +209,10 @@ to.

    132. modules |
    133. - + + + + @@ -44,9 +49,12 @@ Fortunately, you don’t have to create the features manually, since it has been we can integrate their work quite easily with Django. I have decided to focus on the Django-wiki.

      -

      Note: this article has been updated for Evennia 0.9. If you’re not yet using this version, be careful, as the django wiki doesn’t support Python 2 anymore. (Remove this note when enough time has passed.)

      +

      Note: this article has been updated for Evennia 0.9. If you’re not yet using this version, be +careful, as the django wiki doesn’t support Python 2 anymore. (Remove this note when enough time +has passed.)

      -

      The Django-wiki offers a lot of features associated with wikis, is +

      The Django-wiki offers a lot of features associated with +wikis, is actively maintained (at this time, anyway), and isn’t too difficult to install in Evennia. You can see a demonstration of Django-wiki here.

      @@ -73,14 +81,17 @@ one you use to run the

      Adding the wiki in the settings

      You will need to add a few settings to have the wiki app on your website. Open your -server/conf/settings.py file and add the following at the bottom (but before importing secret_settings). Here’s what you’ll find in my own setting file (add the whole Django-wiki section):

      +server/conf/settings.py file and add the following at the bottom (but before importing +secret_settings). Here’s what you’ll find in my own setting file (add the whole Django-wiki +section):

       1
        2
        3
      @@ -233,7 +244,8 @@ who can write, a specific article.

      These settings must be placed, as usual, in your server/conf/settings.py file. They take a function as argument, said function (or callback) will be called with the article and the user. Remember, a Django user, for us, is an account. So we could check lockstrings on them if needed. -Here is a default setting to restrict the wiki: only builders can write in it, but anyone (including non-logged in users) can read it. The superuser has some additional privileges.

      +Here is a default setting to restrict the wiki: only builders can write in it, but anyone (including +non-logged in users) can read it. The superuser has some additional privileges.

       1
        2
        3
      @@ -298,7 +310,9 @@ Here is a default setting to restrict the wiki: only builders can write in it, b
       

      Here, we have created three functions: one to return True if the user is the superuser, one to -return True if the user is a builder, one to return True no matter what (this includes if the user is anonymous, E.G. if it’s not logged-in). We then change settings to allow either the superuser or +return True if the user is a builder, one to return True no matter what (this includes if the +user is anonymous, E.G. if it’s not logged-in). We then change settings to allow either the +superuser or each builder to moderate, read, write, delete, and more. You can, of course, add more functions, adapting them to your need. This is just a demonstration.

      Providing the WIKI_CAN*... settings will bypass the original permission system. The superuser @@ -306,23 +320,35 @@ could change permissions of an article, but still, only builders would be able t need something more custom, you will have to expand on the functions you use.

      Managing wiki pages from Evennia

      -

      Unfortunately, Django wiki doesn’t provide a clear and clean entry point to read and write articles from Evennia and it doesn’t seem to be a very high priority. If you really need to keep Django wiki and to create and manage wiki pages from your code, you can do so, but this article won’t elaborate, as this is somewhat more technical.

      -

      However, it is a good opportunity to present a small project that has been created more recently: evennia-wiki has been created to provide a simple wiki, more tailored to Evennia and easier to connect. It doesn’t, as yet, provide as many options as does Django wiki, but it’s perfectly usable:

      +

      Unfortunately, Django wiki doesn’t provide a clear and clean entry point to read and write articles +from Evennia and it doesn’t seem to be a very high priority. If you really need to keep Django wiki +and to create and manage wiki pages from your code, you can do so, but this article won’t elaborate, +as this is somewhat more technical.

      +

      However, it is a good opportunity to present a small project that has been created more recently: +evennia-wiki has been created to provide a simple +wiki, more tailored to Evennia and easier to connect. It doesn’t, as yet, provide as many options +as does Django wiki, but it’s perfectly usable:

      • Pages have an inherent and much-easier to understand hierarchy based on URLs.

      • -
      • Article permissions are connected to Evennia groups and are much easier to accommodate specific requirements.

      • +
      • Article permissions are connected to Evennia groups and are much easier to accommodate specific +requirements.

      • Articles can easily be created, read or updated from the Evennia code itself.

      • -
      • Markdown is fully-supported with a default integration to Bootstrap to look good on an Evennia website. Tables and table of contents are supported as well as wiki links.

      • +
      • Markdown is fully-supported with a default integration to Bootstrap to look good on an Evennia +website. Tables and table of contents are supported as well as wiki links.

      • The process to override wiki templates makes full use of the template_overrides directory.

      However evennia-wiki doesn’t yet support:

        -
      • Images in markdown and the uploading schema. If images are important to you, please consider contributing to this new project.

      • +
      • Images in markdown and the uploading schema. If images are important to you, please consider +contributing to this new project.

      • Modifying permissions on a per page/setting basis.

      • Moving pages to new locations.

      • Viewing page history.

      -

      Considering the list of features in Django wiki, obviously other things could be added to the list. However, these features may be the most important and useful. Additional ones might not be that necessary. If you’re interested in supporting this little project, you are more than welcome to contribute to it. Thanks!

      +

      Considering the list of features in Django wiki, obviously other things could be added to the list. +However, these features may be the most important and useful. Additional ones might not be that +necessary. If you’re interested in supporting this little project, you are more than welcome to +contribute to it. Thanks!

      @@ -390,7 +416,10 @@ need something more custom, you will have to expand on the functions you use.

      modules | - + + + + @@ -42,12 +47,16 @@ it just mygame

      Step 1: Creating a custom command

        -
      1. Open mygame/commands/command.py in a text editor. This is just one place commands could be placed but you get it setup from the onset as an easy place to start. It also already contains some example code.

      2. +
      3. Open mygame/commands/command.py in a text editor. This is just one place commands could be +placed but you get it setup from the onset as an easy place to start. It also already contains some +example code.

      4. Create a new class in command.py inheriting from default_cmds.MuxCommand. Let’s call it CmdEcho in this example.

      5. Set the class variable key to a good command name, like echo.

      6. -
      7. Give your class a useful docstring. A docstring is the string at the very top of a class or function/method. The docstring at the top of the command class is read by Evennia to become the help entry for the Command (see -Command Auto-help).

      8. +
      9. Give your class a useful docstring. A docstring is the string at the very top of a class or +function/method. The docstring at the top of the command class is read by Evennia to become the help +entry for the Command (see +Command Auto-help).

      10. Define a class method func(self) that echoes your input back to you.

      Below is an example how this all could look for the echo command:

      @@ -145,8 +154,10 @@ the game. Use help<

      If you have trouble, make sure to check the log for error messages (probably due to syntax errors in your command definition).

      -

      Note: Typing echotest will also work. It will be handled as the command echo directly followed by -its argument test (which will end up in self.args). To change this behavior, you can add the arg_regexproperty alongsidekey, help_category` etc. See the arg_regex documentation for more info.

      +

      Note: Typing echotest will also work. It will be handled as the command echo directly followed +by +its argument test (which will end up in self.args). To change this behavior, you can add the arg_regexproperty alongsidekey, help_category` etc. See the arg_regex +documentation for more info.

      If you want to overload existing default commands (such as look or get), just add your new command with the same key as the old one - it will then replace it. Just remember that you must use @@ -219,9 +230,10 @@ object is first created. This means that if you already have existing objects in using that typeclass, they will not have been initiated the same way. There are many ways to update them; since it’s a one-time update you can usually just simply loop through them. As superuser, try the following:

      -
       @py from typeclasses.objects import MyObject; [o.cmdset.add("mycmdset.MyCmdSet") for o in MyObject.objects.all()]
      +
       @py from typeclasses.objects import MyObject; [o.cmdset.add("mycmdset.MyCmdSet") for o in
       
      +

      MyObject.objects.all()]

      This goes through all objects in your database having the right typeclass, adding the new cmdset to each. The good news is that you only have to do this if you want to post-add cmdsets. If you just want to add a new command, you can simply add that command to the cmdset’s at_cmdset_creation @@ -295,7 +307,10 @@ default character cmdset defaults to being defined as

    134. modules |
    135. - + + + +
      @@ -212,7 +217,10 @@ objects you can use modules | - + + + +
      @@ -36,7 +41,8 @@

      Administrative Docs

      -

      The following pages are aimed at game administrators – the higher-ups that possess shell access and are responsible for managing the game.

      +

      The following pages are aimed at game administrators – the higher-ups that possess shell access and +are responsible for managing the game.

      Customizing the server

      @@ -152,7 +159,10 @@
    136. modules |
    137. - + + + +
      @@ -52,14 +57,16 @@ you would like covered, please let us know.

      Install mod_wsgi

        -
      • Fedora/RHEL - Apache HTTP Server and mod_wsgi are available in the standard package repositories for Fedora and RHEL:

        +
      • Fedora/RHEL - Apache HTTP Server and mod_wsgi are available in the standard package +repositories for Fedora and RHEL:

        $ dnf install httpd mod_wsgi
         or
         $ yum install httpd mod_wsgi
         
      • -
      • Ubuntu/Debian - Apache HTTP Server and mod_wsgi are available in the standard package repositories for Ubuntu and Debian:

        +
      • Ubuntu/Debian - Apache HTTP Server and mod_wsgi are available in the standard package +repositories for Ubuntu and Debian:

        $ apt-get update
         $ apt-get install apache2 libapache2-mod-wsgi
         
        @@ -104,7 +111,8 @@ Ubuntu), you may tell evennia/game/web/utils/apache_wsgi.conf. When mod_wsgi sees that the file modification time has changed, it will force a code reload. Any modifications to the code will not be propagated to the live instance of your site until reloaded.

        -

        If you are not running in daemon mode or want to force the issue, simply restart or reload apache2 to apply your changes.

        +

        If you are not running in daemon mode or want to force the issue, simply restart or reload apache2 +to apply your changes.

        Further notes and hints:

        @@ -125,20 +133,24 @@ Not confirmed, but worth trying if there are trouble.

        mod_proxy and mod_ssl setup

        Below are steps on running Evennia using a front-end proxy (Apache HTTP), mod_proxy_http, -mod_proxy_wstunnel, and mod_ssl. mod_proxy_http and mod_proxy_wstunnel will simply be referred to as +mod_proxy_wstunnel, and mod_ssl. mod_proxy_http and mod_proxy_wstunnel will simply be +referred to as mod_proxy below.

        Install mod_ssl

          -
        • Fedora/RHEL - Apache HTTP Server and mod_ssl are available in the standard package repositories for Fedora and RHEL:

          +
        • Fedora/RHEL - Apache HTTP Server and mod_ssl are available in the standard package +repositories for Fedora and RHEL:

          $ dnf install httpd mod_ssl
           or
           $ yum install httpd mod_ssl
           
        • -
        • Ubuntu/Debian - Apache HTTP Server and mod_ssljkl are installed together in the apache2 package and available in the -standard package repositories for Ubuntu and Debian. mod_ssl needs to be enabled after installation:

          +
        • Ubuntu/Debian - Apache HTTP Server and mod_ssljkl are installed together in the apache2 +package and available in the +standard package repositories for Ubuntu and Debian. mod_ssl needs to be enabled after +installation:

          $ apt-get update
           $ apt-get install apache2 
           $ a2enmod ssl
          @@ -273,7 +285,10 @@ port but this should be applicable also to other types of proxies (like nginx).<
                   
        • modules |
        • - + + + +
      • modules |
      • - + + + +
      @@ -55,16 +60,20 @@ better match with the vanilla Evennia install.

      Installing Evennia

      Firstly, set aside a folder/directory on your drive for everything to follow.

      -

      You need to start by installing Evennia by following most of the Getting Started -Instructions for your OS. The difference is that you need to git clone https://github.com/TehomCD/evennia.git instead of Evennia’s repo because Arx uses TehomCD’s older Evennia 0.8 fork, notably still using Python2. This detail is important if referring to newer Evennia documentation.

      +

      You need to start by installing Evennia by following most of the Getting +Started +Instructions for your OS. The difference is that you need to git clone https://github.com/TehomCD/evennia.git instead of Evennia’s repo because Arx uses TehomCD’s older +Evennia 0.8 fork, notably still using Python2. This detail is +important if referring to newer Evennia documentation.

      If you are new to Evennia it’s highly recommended that you run through the instructions in full - including initializing and starting a new empty game and connecting to it. That way you can be sure Evennia works correctly as a base line. If you have trouble, make sure to -read the Troubleshooting instructions for your +read the Troubleshooting instructions for your operating system. You can also drop into our forums, join #evennia on irc.freenode.net or chat from the linked Discord Server.

      -

      After installing you should have a virtualenv running and you should have the following file structure in your set-aside folder:

      +

      After installing you should have a virtualenv running and you should have the following file +structure in your set-aside folder:

      vienv/
       evennia/
       mygame/
      @@ -85,7 +94,8 @@ to compare to.

      A new folder myarx should appear next to the ones you already had. You could rename this to something else if you want.

      -

      Cd into myarx. If you wonder about the structure of the game dir, you can read more about it here.

      +

      Cd into myarx. If you wonder about the structure of the game dir, you can read more about it +here.

      Clean up settings

      @@ -109,7 +119,8 @@ way but remove the secret-handling and replace it with the normal Evennia method

      Note: Indents and capitalization matter in Python. Make indents 4 spaces (not tabs) for your own -sanity. If you want a starter on Python in Evennia, you can look here.

      +sanity. If you want a starter on Python in Evennia, [you can look here](Python-basic- +introduction).

      This will import Arx’ base settings and override them with the Evennia-default telnet port and give the game a name. The slogan changes the sub-text shown under the name of your game in the website @@ -190,7 +201,8 @@ up an Admin Group in Django).

      account. Move to where you want the new staffer character to appear.

    138. In the game client, run @create/drop <staffername>:typeclasses.characters.Character, where <staffername> is usually the same name you used for the Staffer account you created in the -Admin earlier (if you are creating a Character for your superuser, use your superuser account name). +Admin earlier (if you are creating a Character for your superuser, use your superuser account +name). This creates a new in-game Character and places it in your current location.

    139. Have the new Admin player log into the game.

    140. Have the new Admin puppet the character with @ic StafferName.

    141. @@ -214,7 +226,9 @@ AssetOwner holds information about a character or organization’s money and res

      Alternate guide by Pax for installing on Windows

      -

      If for some reason you cannot use the Windows Subsystem for Linux (which would use instructions identical to the ones above), it’s possible to get Evennia running under Anaconda for Windows. The process is a little bit trickier.

      +

      If for some reason you cannot use the Windows Subsystem for Linux (which would use instructions +identical to the ones above), it’s possible to get Evennia running under Anaconda for Windows. The +process is a little bit trickier.

      Make sure you have:

      @@ -102,7 +107,8 @@ above) finishes successfully. The argument at_return_kwargs - an optional dictionary that will be fed as keyword arguments to the at_return callback.

      +
    142. at_return_kwargs - an optional dictionary that will be fed as keyword arguments to the +at_return callback.

    143. 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 @@ -137,7 +143,8 @@ errback.

    144. 17 18 19 -20
          from evennia import utils, Command
      +20
      +21
          from evennia import utils, Command
       
           class CmdAsync(Command):
       
      @@ -156,11 +163,13 @@ errback.

      self.caller.msg("There was an error: %s" % e) # do the async call, setting all callbacks - utils.run_async(long_running_function, at_return=at_return_function, at_err=at_err_function) + utils.run_async(long_running_function, at_return=at_return_function, +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 +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.

      @@ -194,7 +203,8 @@ the

      The @interactive decorator

      -

      As of Evennia 0.9, 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.

       1
      @@ -282,7 +292,8 @@ 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 object; the wrapper sets +Twisted Deferred 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.

      @@ -347,7 +358,10 @@ your own liking.

    145. modules |
    146. - + + + +
      @@ -48,7 +53,8 @@ data in properties already defined on entities (such as key property no matter how hard you tried). Attributes come into play when you want to assign arbitrary data to arbitrary names.

      -

      Attributes are not secure by default and any player may be able to change them unless you prevent this behavior.

      +

      Attributes are not secure by default and any player may be able to change them unless you +prevent this behavior.

      The .db and .ndb shortcuts

      To save persistent data on a Typeclassed object you normally use the db (DataBase) operator. Let’s @@ -83,7 +89,8 @@ use ndbndb (as well as db) will also be easily listed by example the @examine command.

      -

      You can also del properties on db and ndb as normal. This will for example delete an Attribute:

      +

      You can also del properties on db and ndb as normal. This will for example delete an +Attribute:

      1
          del rose.db.has_thorns
       
      @@ -118,7 +125,7 @@ supplied here to restrict future access and also the call itself may be checked before performing the deletion. - clear(...) - removes all Attributes from object.

    147. all(...) - returns all Attributes (of the given category) attached to this object.

    148. -

      See this section for more about locking down Attribute +

      See this section for more about locking down Attribute access and editing. The Nattribute offers no concept of access control.

      Some examples:

      1
      @@ -147,16 +154,19 @@ access and editing. The attrname.

    149. value - this is the value of the Attribute. This value can be anything which can be pickled - objects, lists, numbers or what have you (see -this section for more info). In the example +this section for more info). In the +example obj.db.attrname = value, the value is stored here.

    150. category - this is an optional property that is set to None for most Attributes. Setting this allows to use Attributes for different functionality. This is usually not needed unless you want -to use Attributes for very different functionality (Nicks is an example of using Attributes -in this way). To modify this property you need to use the Attribute Handler.

    151. +to use Attributes for very different functionality (Nicks is an example of using +Attributes +in this way). To modify this property you need to use the Attribute +Handler.

    152. strvalue - this is a separate value field that only accepts strings. This severely limits the data possible to store, but allows for easier database lookups. This property is usually not used except when re-using Attributes for some other purpose (Nicks use it). It is only -accessible via the Attribute Handler.

    153. +accessible via the Attribute Handler.

      There are also two special properties:

        @@ -181,13 +191,15 @@ useful in a few situations though.

        this is not an issue unless you are reading and writing to your Attribute very often (like many times per second). Reading from an already cached Attribute is as fast as reading any Python property. But even then this is not likely something to worry about: Apart from Evennia’s own -caching, modern database systems themselves also cache data very efficiently for speed. Our default +caching, modern database systems themselves also cache data very efficiently for speed. Our +default database even runs completely in RAM if possible, alleviating much of the need to write to disk during heavy loads.

      • A more valid reason for using non-persistent data is if you want to lose your state when logging off. Maybe you are storing throw-away data that are re-initialized at server startup. Maybe you are implementing some caching of your own. Or maybe you are testing a buggy Script that -does potentially harmful stuff to your character object. With non-persistent storage you can be sure +does potentially harmful stuff to your character object. With non-persistent storage you can be +sure that whatever is messed up, it’s nothing a server reboot can’t clear up.

      • NAttributes have no restrictions at all on what they can store (see next section), since they don’t need to worry about being saved to the database - they work very well for temporary storage.

      • @@ -216,10 +228,12 @@ not a big deal. But if you are accessing the Attribute as part of some big loop amount of reads/writes you should first extract it to a temporary variable, operate on that and then save the result back to the Attribute. If you are storing a more complex structure like a dict or a list you should make sure to “disconnect” it from the database before looping over it, -as mentioned in the Retrieving Mutable Objects section below.

        +as mentioned in the Retrieving Mutable Objects section +below.

        Storing single objects

        -

        With a single object, we mean anything that is not iterable, like numbers, strings or custom class instances without the __iter__ method.

        +

        With a single object, we mean anything that is not iterable, like numbers, strings or custom class +instances without the __iter__ method.

        • You can generally store any non-iterable Python entity that can be pickled.

        • @@ -268,14 +282,21 @@ entities you can loop over in a for-loop. Attribute-saving supports the followin
        • Lists, like [1,2,"test", <dbobj>].

        • Dicts, like {1:2, "test":<dbobj>].

        • Sets, like {1,2,"test",<dbobj>}.

        • -
        • collections.OrderedDict, like OrderedDict((1,2), ("test", <dbobj>)).

        • -
        • collections.Deque, like deque((1,2,"test",<dbobj>)).

        • -
        • Nestings of any combinations of the above, like lists in dicts or an OrderedDict of tuples, each containing dicts, etc.

        • +
        • +
        +

        collections.OrderedDict, +like OrderedDict((1,2), ("test", <dbobj>)).

        +
          +
        • collections.Deque, like +deque((1,2,"test",<dbobj>)).

        • +
        • Nestings of any combinations of the above, like lists in dicts or an OrderedDict of tuples, each +containing dicts, etc.

        • All other iterables (i.e. entities with the __iter__ method) will be converted to a list. Since you can use any combination of the above iterables, this is generally not much of a limitation.

        -

        Any entity listed in the Single object section above can be stored in the iterable.

        +

        Any entity listed in the Single object section above can be +stored in the iterable.

        As mentioned in the previous section, database entities (aka typeclasses) are not possible to pickle. So when storing an iterable, Evennia must recursively traverse the iterable and all its @@ -400,7 +421,8 @@ way.

        Locking and checking Attributes

        Attributes are normally not locked down by default, but you can easily change that for individual Attributes (like those that may be game-sensitive in games with user-level building).

        -

        First you need to set a lock string on your Attribute. Lock strings are specified Locks. The relevant lock types are

        +

        First you need to set a lock string on your Attribute. Lock strings are specified Locks. +The relevant lock types are

      @@ -41,7 +46,8 @@ no other recourse but to kick out a particularly troublesome player. The default 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 +

      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.

      @@ -50,50 +56,68 @@ have tried to be nice. Now you just want this troll gone.

       @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!

      -

      You can also give a reason so you remember later why this was a good thing (the banned account will never see this)

      +

      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)

       @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:

       @delaccount YouSuck
       
      -

      Generally, banning the name is the easier and safer way to stop the use of an account – if you change your mind you can always remove the block later whereas a deletion is permanent.

      +

      Generally, banning the name is the easier and safer way to stop the use of an account – if you +change your mind you can always remove the block later whereas a deletion is permanent.

      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.

      -

      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:

      +

      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:

       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.

      -

      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.

      +

      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.

      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 
       1    yousuck       Fri Jan 3 23:00:22 2020   This is just a Troll.
      @@ -112,8 +136,11 @@ have tried to be nice. Now you just want this troll gone.

      Summary of abuse-handling tools

      Below are other useful commands for dealing with annoying players.

        -
      • who – (as admin) Find the IP of a account. Note that one account can be connected to from multiple IPs depending on what you allow in your settings.

      • -
      • examine/account thomas – Get all details about an account. You can also use *thomas to get the account. If not given, you will get the Object thomas if it exists in the same location, which is not what you want in this case.

      • +
      • who – (as admin) Find the IP of a account. Note that one account can be connected to from +multiple IPs depending on what you allow in your settings.

      • +
      • examine/account thomas – Get all details about an account. You can also use *thomas to get +the account. If not given, you will get the Object thomas if it exists in the same location, which +is not what you want in this case.

      • boot thomas – Boot all sessions of the given account name.

      • boot 23 – Boot one specific client session/IP by its unique id.

      • ban – List all bans (listed with ids)

      • @@ -123,25 +150,34 @@ have tried to be nice. Now you just want this troll gone.

      • ban/ip 134.233.*.* – Even wider IP ban

      • 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.

      • +
      • clock mychannel = control:perm(Admin);listen:all();send:all() – Fine control of access to +your channel using lock definitions.

      Locking a specific command (like page) is accomplished like so:

        -
      1. Examine the source of the command. The default page command class 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 or Tag, your current location etc.

      2. -
      3. perm/account thomas = page_banned – Give the account the ‘permission’ which causes (in this case) the lock to fail.

      4. +
      5. Examine the source of the command. The default page command class 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 or Tag, your +current location etc.

      6. +
      7. 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

      • delaccount 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.

      @@ -208,7 +244,10 @@ have tried to be nice. Now you just want this troll gone.

    154. modules |
    155. - + + + +
      @@ -36,34 +41,59 @@

      Batch Code Processor

      -

      For an introduction and motivation to using batch processors, see here. This page describes the Batch-code processor. The Batch-command one is covered here.

      +

      For an introduction and motivation to using batch processors, see here. This +page describes the Batch-code processor. The Batch-command one is covered [here](Batch-Command- +Processor).

      Basic Usage

      The batch-code processor is a superuser-only function, invoked by

       > @batchcode path.to.batchcodefile
       
      -

      Where path.to.batchcodefile is the path to a batch-code file. Such a file should have a name ending in “.py” (but you shouldn’t include that in the path). The path is given like a python path relative to a folder you define to hold your batch files, set by BATCH_IMPORT_PATH in your settings. Default folder is (assuming your game is called “mygame”) mygame/world/. So if you want to run the example batch file in mygame/world/batch_code.py, you could simply use

      +

      Where path.to.batchcodefile is the path to a batch-code file. Such a file should have a name +ending in “.py” (but you shouldn’t include that in the path). The path is given like a python path +relative to a folder you define to hold your batch files, set by BATCH_IMPORT_PATH in your +settings. Default folder is (assuming your game is called “mygame”) mygame/world/. So if you want +to run the example batch file in mygame/world/batch_code.py, you could simply use

       > @batchcode batch_code
       
      -

      This will try to run through the entire batch file in one go. For more gradual, interactive control you can use the /interactive switch. The switch /debug will put the processor in debug mode. Read below for more info.

      +

      This will try to run through the entire batch file in one go. For more gradual, interactive +control you can use the /interactive switch. The switch /debug will put the processor in +debug mode. Read below for more info.

      The batch file

      -

      A batch-code file is a normal Python file. The difference is that since the batch processor loads and executes the file rather than importing it, you can reliably update the file, then call it again, over and over and see your changes without needing to @reload the server. This makes for easy testing. In the batch-code file you have also access to the following global variables:

      +

      A batch-code file is a normal Python file. The difference is that since the batch processor loads +and executes the file rather than importing it, you can reliably update the file, then call it +again, over and over and see your changes without needing to @reload the server. This makes for +easy testing. In the batch-code file you have also access to the following global variables:

      • caller - This is a reference to the object running the batchprocessor.

      • -
      • DEBUG - This is a boolean that lets you determine if this file is currently being run in debug-mode or not. See below how this can be useful.

      • +
      • DEBUG - This is a boolean that lets you determine if this file is currently being run in debug- +mode or not. See below how this can be useful.

      -

      Running a plain Python file through the processor will just execute the file from beginning to end. If you want to get more control over the execution you can use the processor’s interactive mode. This runs certain code blocks on their own, rerunning only that part until you are happy with it. In order to do this you need to add special markers to your file to divide it up into smaller chunks. These take the form of comments, so the file remains valid Python.

      +

      Running a plain Python file through the processor will just execute the file from beginning to end. +If you want to get more control over the execution you can use the processor’s interactive mode. +This runs certain code blocks on their own, rerunning only that part until you are happy with it. In +order to do this you need to add special markers to your file to divide it up into smaller chunks. +These take the form of comments, so the file remains valid Python.

      Here are the rules of syntax of the batch-code *.py file.

        -
      • #CODE as the first on a line marks the start of a code block. It will last until the beginning of another marker or the end of the file. Code blocks contain functional python code. Each #CODE block will be run in complete isolation from other parts of the file, so make sure it’s self-contained.

      • -
      • #HEADER as the first on a line marks the start of a header block. It lasts until the next marker or the end of the file. This is intended to hold imports and variables you will need for all other blocks .All python code defined in a header block will always be inserted at the top of every #CODE blocks in the file. You may have more than one #HEADER block, but that is equivalent to having one big one. Note that you can’t exchange data between code blocks, so editing a header-variable in one code block won’t affect that variable in any other code block!

      • +
      • #CODE as the first on a line marks the start of a code block. It will last until the beginning +of another marker or the end of the file. Code blocks contain functional python code. Each #CODE +block will be run in complete isolation from other parts of the file, so make sure it’s self- +contained.

      • +
      • #HEADER as the first on a line marks the start of a header block. It lasts until the next +marker or the end of the file. This is intended to hold imports and variables you will need for all +other blocks .All python code defined in a header block will always be inserted at the top of every +#CODE blocks in the file. You may have more than one #HEADER block, but that is equivalent to +having one big one. Note that you can’t exchange data between code blocks, so editing a header- +variable in one code block won’t affect that variable in any other code block!

      • #INSERT path.to.file will insert another batchcode (Python) file at that position.

      • A # that is not starting a #HEADER, #CODE or #INSERT instruction is considered a comment.

      • -
      • Inside a block, normal Python syntax rules apply. For the sake of indentation, each block acts as a separate python module.

      • +
      • Inside a block, normal Python syntax rules apply. For the sake of indentation, each block acts as +a separate python module.

      Below is a version of the example file found in evennia/contrib/tutorial_examples/.

       1
      @@ -151,12 +181,22 @@
       
       > @batchcode/debug tutorial_examples.example_batch_code
       
      -

      The batch script will run to the end and tell you it completed. You will also get messages that the button and the two pieces of furniture were created. Look around and you should see the button there. But you won’t see any chair nor a table! This is because we ran this with the /debug switch, which is directly visible as DEBUG==True inside the script. In the above example we handled this state by deleting the chair and table again.

      -

      The debug mode is intended to be used when you test out a batchscript. Maybe you are looking for bugs in your code or try to see if things behave as they should. Running the script over and over would then create an ever-growing stack of chairs and tables, all with the same name. You would have to go back and painstakingly delete them later.

      +

      The batch script will run to the end and tell you it completed. You will also get messages that the +button and the two pieces of furniture were created. Look around and you should see the button +there. But you won’t see any chair nor a table! This is because we ran this with the /debug +switch, which is directly visible as DEBUG==True inside the script. In the above example we +handled this state by deleting the chair and table again.

      +

      The debug mode is intended to be used when you test out a batchscript. Maybe you are looking for +bugs in your code or try to see if things behave as they should. Running the script over and over +would then create an ever-growing stack of chairs and tables, all with the same name. You would have +to go back and painstakingly delete them later.

      Interactive mode

      -

      Interactive mode works very similar to the batch-command processor counterpart. It allows you more step-wise control over how the batch file is executed. This is useful for debugging or for picking and choosing only particular blocks to run. Use @batchcode with the /interactive flag to enter interactive mode.

      +

      Interactive mode works very similar to the [batch-command processor counterpart](Batch-Command- +Processor). It allows you more step-wise control over how the batch file is executed. This is useful +for debugging or for picking and choosing only particular blocks to run. Use @batchcode with the +/interactive flag to enter interactive mode.

       > @batchcode/interactive tutorial_examples.batch_code
       
      @@ -164,8 +204,10 @@
      01/02: red_button = create_object(red_button.RedButton, [...]         (hh for help) 
       
      -

      This shows that you are on the first #CODE block, the first of only two commands in this batch file. Observe that the block has not actually been executed at this point!

      -

      To take a look at the full code snippet you are about to run, use ll (a batch-processor version of look).

      +

      This shows that you are on the first #CODE block, the first of only two commands in this batch +file. Observe that the block has not actually been executed at this point!

      +

      To take a look at the full code snippet you are about to run, use ll (a batch-processor version of +look).

       1
        2
        3
      @@ -189,24 +231,52 @@
           caller.msg("A red button was created.")
       
      -

      Compare with the example code given earlier. Notice how the content of #HEADER has been pasted at the top of the #CODE block. Use pp to actually execute this block (this will create the button and give you a message). Use nn (next) to go to the next command. Use hh for a list of commands.

      -

      If there are tracebacks, fix them in the batch file, then use rr to reload the file. You will still be at the same code block and can rerun it easily with pp as needed. This makes for a simple debug cycle. It also allows you to rerun individual troublesome blocks - as mentioned, in a large batch file this can be very useful (don’t forget the /debug mode either).

      -

      Use nn and bb (next and back) to step through the file; e.g. nn 12 will jump 12 steps forward (without processing any blocks in between). All normal commands of Evennia should work too while working in interactive mode.

      +

      Compare with the example code given earlier. Notice how the content of #HEADER has been pasted at +the top of the #CODE block. Use pp to actually execute this block (this will create the button +and give you a message). Use nn (next) to go to the next command. Use hh for a list of commands.

      +

      If there are tracebacks, fix them in the batch file, then use rr to reload the file. You will +still be at the same code block and can rerun it easily with pp as needed. This makes for a simple +debug cycle. It also allows you to rerun individual troublesome blocks - as mentioned, in a large +batch file this can be very useful (don’t forget the /debug mode either).

      +

      Use nn and bb (next and back) to step through the file; e.g. nn 12 will jump 12 steps forward +(without processing any blocks in between). All normal commands of Evennia should work too while +working in interactive mode.

      Limitations and Caveats

      -

      The batch-code processor is by far the most flexible way to build a world in Evennia. There are however some caveats you need to keep in mind.

      +

      The batch-code processor is by far the most flexible way to build a world in Evennia. There are +however some caveats you need to keep in mind.

      Safety

      -

      Or rather the lack of it. There is a reason only superusers are allowed to run the batch-code processor by default. The code-processor runs without any Evennia security checks and allows full access to Python. If an untrusted party could run the code-processor they could execute arbitrary python code on your machine, which is potentially a very dangerous thing. If you want to allow other users to access the batch-code processor you should make sure to run Evennia as a separate and very limited-access user on your machine (i.e. in a ‘jail’). By comparison, the batch-command processor is much safer since the user running it is still ‘inside’ the game and can’t really do anything outside what the game commands allow them to.

      +

      Or rather the lack of it. There is a reason only superusers are allowed to run the batch-code +processor by default. The code-processor runs without any Evennia security checks and allows +full access to Python. If an untrusted party could run the code-processor they could execute +arbitrary python code on your machine, which is potentially a very dangerous thing. If you want to +allow other users to access the batch-code processor you should make sure to run Evennia as a +separate and very limited-access user on your machine (i.e. in a ‘jail’). By comparison, the batch- +command processor is much safer since the user running it is still ‘inside’ the game and can’t +really do anything outside what the game commands allow them to.

      No communication between code blocks

      -

      Global variables won’t work in code batch files, each block is executed as stand-alone environments. #HEADER blocks are literally pasted on top of each #CODE block so updating some header-variable in your block will not make that change available in another block. Whereas a python execution limitation, allowing this would also lead to very hard-to-debug code when using the interactive mode - this would be a classical example of “spaghetti code”.

      -

      The main practical issue with this is when building e.g. a room in one code block and later want to connect that room with a room you built in the current block. There are two ways to do this:

      +

      Global variables won’t work in code batch files, each block is executed as stand-alone environments. +#HEADER blocks are literally pasted on top of each #CODE block so updating some header-variable +in your block will not make that change available in another block. Whereas a python execution +limitation, allowing this would also lead to very hard-to-debug code when using the interactive mode

      +
        +
      • this would be a classical example of “spaghetti code”.

      • +
      +

      The main practical issue with this is when building e.g. a room in one code block and later want to +connect that room with a room you built in the current block. There are two ways to do this:

        -
      • Perform a database search for the name of the room you created (since you cannot know in advance which dbref it got assigned). The problem is that a name may not be unique (you may have a lot of “A dark forest” rooms). There is an easy way to handle this though - use Tags or Aliases. You can assign any number of tags and/or aliases to any object. Make sure that one of those tags or aliases is unique to the room (like “room56”) and you will henceforth be able to always uniquely search and find it later.

      • -
      • Use the caller global property as an inter-block storage. For example, you could have a dictionary of room references in an ndb:

        +
      • Perform a database search for the name of the room you created (since you cannot know in advance +which dbref it got assigned). The problem is that a name may not be unique (you may have a lot of “A +dark forest” rooms). There is an easy way to handle this though - use Tags or Aliases. You +can assign any number of tags and/or aliases to any object. Make sure that one of those tags or +aliases is unique to the room (like “room56”) and you will henceforth be able to always uniquely +search and find it later.

      • +
      • Use the caller global property as an inter-block storage. For example, you could have a +dictionary of room references in an ndb:

         1
          2
          3
        @@ -234,15 +304,23 @@
         
      -

      Note how we check in #HEADER if caller.ndb.all_rooms doesn’t already exist before creating the dict. Remember that #HEADER is copied in front of every #CODE block. Without that if statement we’d be wiping the dict every block!

      +

      Note how we check in #HEADER if caller.ndb.all_rooms doesn’t already exist before creating the +dict. Remember that #HEADER is copied in front of every #CODE block. Without that if statement +we’d be wiping the dict every block!

      Don’t treat a batchcode file like any Python file

      -

      Despite being a valid Python file, a batchcode file should only be run by the batchcode processor. You should not do things like define Typeclasses or Commands in them, or import them into other code. Importing a module in Python will execute base level of the module, which in the case of your average batchcode file could mean creating a lot of new objects every time.

      +

      Despite being a valid Python file, a batchcode file should only be run by the batchcode processor. +You should not do things like define Typeclasses or Commands in them, or import them into other +code. Importing a module in Python will execute base level of the module, which in the case of your +average batchcode file could mean creating a lot of new objects every time.

      Don’t let code rely on the batch-file’s real file path

      -

      When you import things into your batchcode file, don’t use relative imports but always import with paths starting from the root of your game directory or evennia library. Code that relies on the batch file’s “actual” location will fail. Batch code files are read as text and the strings executed. When the code runs it has no knowledge of what file those strings where once a part of.

      +

      When you import things into your batchcode file, don’t use relative imports but always import with +paths starting from the root of your game directory or evennia library. Code that relies on the +batch file’s “actual” location will fail. Batch code files are read as text and the strings +executed. When the code runs it has no knowledge of what file those strings where once a part of.

      @@ -310,7 +388,10 @@
    156. modules |
    157. - + + + + @@ -36,30 +41,52 @@

      Batch Command Processor

      -

      For an introduction and motivation to using batch processors, see here. This page describes the Batch-command processor. The Batch-code one is covered here.

      +

      For an introduction and motivation to using batch processors, see here. This +page describes the Batch-command processor. The Batch-code one is covered [here](Batch-Code- +Processor).

      Basic Usage

      The batch-command processor is a superuser-only function, invoked by

       > @batchcommand path.to.batchcmdfile
       
      -

      Where path.to.batchcmdfile is the path to a batch-command file with the “.ev” file ending. This path is given like a python path relative to a folder you define to hold your batch files, set with BATCH_IMPORT_PATH in your settings. Default folder is (assuming your game is in the mygame folder) mygame/world. So if you want to run the example batch file in mygame/world/batch_cmds.ev, you could use

      +

      Where path.to.batchcmdfile is the path to a batch-command file with the “.ev” file ending. +This path is given like a python path relative to a folder you define to hold your batch files, set +with BATCH_IMPORT_PATH in your settings. Default folder is (assuming your game is in the mygame +folder) mygame/world. So if you want to run the example batch file in +mygame/world/batch_cmds.ev, you could use

       > @batchcommand batch_cmds
       
      -

      A batch-command file contains a list of Evennia in-game commands separated by comments. The processor will run the batch file from beginning to end. Note that it will not stop if commands in it fail (there is no universal way for the processor to know what a failure looks like for all different commands). So keep a close watch on the output, or use Interactive mode (see below) to run the file in a more controlled, gradual manner.

      +

      A batch-command file contains a list of Evennia in-game commands separated by comments. The +processor will run the batch file from beginning to end. Note that it will not stop if commands in +it fail (there is no universal way for the processor to know what a failure looks like for all +different commands). So keep a close watch on the output, or use Interactive mode (see below) to +run the file in a more controlled, gradual manner.

      The batch file

      -

      The batch file is a simple plain-text file containing Evennia commands. Just like you would write them in-game, except you have more freedom with line breaks.

      +

      The batch file is a simple plain-text file containing Evennia commands. Just like you would write +them in-game, except you have more freedom with line breaks.

      Here are the rules of syntax of an *.ev file. You’ll find it’s really, really simple:

        -
      • All lines having the # (hash)-symbol as the first one on the line are considered comments. All non-comment lines are treated as a command and/or their arguments.

      • -
      • Comment lines have an actual function – they mark the end of the previous command definition. So never put two commands directly after one another in the file - separate them with a comment, or the second of the two will be considered an argument to the first one. Besides, using plenty of comments is good practice anyway.

      • -
      • A line that starts with the word #INSERT is a comment line but also signifies a special instruction. The syntax is #INSERT <path.batchfile> and tries to import a given batch-cmd file into this one. The inserted batch file (file ending .ev) will run normally from the point of the #INSERT instruction.

      • -
      • Extra whitespace in a command definition is ignored. - A completely empty line translates in to a line break in texts. Two empty lines thus means a new paragraph (this is obviously only relevant for commands accepting such formatting, such as the @desc command).

      • +
      • All lines having the # (hash)-symbol as the first one on the line are considered comments. +All non-comment lines are treated as a command and/or their arguments.

      • +
      • Comment lines have an actual function – they mark the end of the previous command definition. +So never put two commands directly after one another in the file - separate them with a comment, or +the second of the two will be considered an argument to the first one. Besides, using plenty of +comments is good practice anyway.

      • +
      • A line that starts with the word #INSERT is a comment line but also signifies a special +instruction. The syntax is #INSERT <path.batchfile> and tries to import a given batch-cmd file +into this one. The inserted batch file (file ending .ev) will run normally from the point of the +#INSERT instruction.

      • +
      • Extra whitespace in a command definition is ignored. - A completely empty line translates in to +a line break in texts. Two empty lines thus means a new paragraph (this is obviously only relevant +for commands accepting such formatting, such as the @desc command).

      • The very last command in the file is not required to end with a comment.

      • -
      • You cannot nest another @batchcommand statement into your batch file. If you want to link many batch-files together, use the #INSERT batch instruction instead. You also cannot launch the @batchcode command from your batch file, the two batch processors are not compatible.

      • +
      • You cannot nest another @batchcommand statement into your batch file. If you want to link many +batch-files together, use the #INSERT batch instruction instead. You also cannot launch the +@batchcode command from your batch file, the two batch processors are not compatible.

      Below is a version of the example file found in evennia/contrib/tutorial_examples/batch_cmds.ev.

       1
      @@ -147,14 +174,20 @@
       
      > @batchcommand contrib.tutorial_examples.batch_cmds
       
      -

      A button will be created, described and dropped in Limbo. All commands will be executed by the user calling the command.

      +

      A button will be created, described and dropped in Limbo. All commands will be executed by the user +calling the command.

      -

      Note that if you interact with the button, you might find that its description changes, loosing your custom-set description above. This is just the way this particular object works.

      +

      Note that if you interact with the button, you might find that its description changes, loosing +your custom-set description above. This is just the way this particular object works.

      Interactive mode

      -

      Interactive mode allows you to more step-wise control over how the batch file is executed. This is useful for debugging and also if you have a large batch file and is only updating a small part of it – running the entire file again would be a waste of time (and in the case of @create-ing objects you would to end up with multiple copies of same-named objects, for example). Use @batchcommand with the /interactive flag to enter interactive mode.

      +

      Interactive mode allows you to more step-wise control over how the batch file is executed. This is +useful for debugging and also if you have a large batch file and is only updating a small part of it +– running the entire file again would be a waste of time (and in the case of @create-ing objects +you would to end up with multiple copies of same-named objects, for example). Use @batchcommand +with the /interactive flag to enter interactive mode.

       > @batchcommand/interactive tutorial_examples.batch_cmds
       
      @@ -162,29 +195,62 @@
      01/04: @create button:tutorial_examples.red_button.RedButton  (hh for help) 
       
      -

      This shows that you are on the @create command, the first out of only four commands in this batch file. Observe that the command @create has not been actually processed at this point!

      -

      To take a look at the full command you are about to run, use ll (a batch-processor version of look). Use pp to actually process the current command (this will actually @create the button) – and make sure it worked as planned. Use nn (next) to go to the next command. Use hh for a list of commands.

      -

      If there are errors, fix them in the batch file, then use rr to reload the file. You will still be at the same command and can rerun it easily with pp as needed. This makes for a simple debug cycle. It also allows you to rerun individual troublesome commands - as mentioned, in a large batch file this can be very useful. Do note that in many cases, commands depend on the previous ones (e.g. if @create in the example above had failed, the following commands would have had nothing to operate on).

      -

      Use nn and bb (next and back) to step through the file; e.g. nn 12 will jump 12 steps forward (without processing any command in between). All normal commands of Evennia should work too while working in interactive mode.

      +

      This shows that you are on the @create command, the first out of only four commands in this batch +file. Observe that the command @create has not been actually processed at this point!

      +

      To take a look at the full command you are about to run, use ll (a batch-processor version of +look). Use pp to actually process the current command (this will actually @create the button) +– and make sure it worked as planned. Use nn (next) to go to the next command. Use hh for a +list of commands.

      +

      If there are errors, fix them in the batch file, then use rr to reload the file. You will still be +at the same command and can rerun it easily with pp as needed. This makes for a simple debug +cycle. It also allows you to rerun individual troublesome commands - as mentioned, in a large batch +file this can be very useful. Do note that in many cases, commands depend on the previous ones (e.g. +if @create in the example above had failed, the following commands would have had nothing to +operate on).

      +

      Use nn and bb (next and back) to step through the file; e.g. nn 12 will jump 12 steps forward +(without processing any command in between). All normal commands of Evennia should work too while +working in interactive mode.

      Limitations and Caveats

      -

      The batch-command processor is great for automating smaller builds or for testing new commands and objects repeatedly without having to write so much. There are several caveats you have to be aware of when using the batch-command processor for building larger, complex worlds though.

      -

      The main issue is that when you run a batch-command script you (you, as in your superuser character) are actually moving around in the game creating and building rooms in sequence, just as if you had been entering those commands manually, one by one. You have to take this into account when creating the file, so that you can ‘walk’ (or teleport) to the right places in order.

      -

      This also means there are several pitfalls when designing and adding certain types of objects. Here are some examples:

      +

      The batch-command processor is great for automating smaller builds or for testing new commands and +objects repeatedly without having to write so much. There are several caveats you have to be aware +of when using the batch-command processor for building larger, complex worlds though.

      +

      The main issue is that when you run a batch-command script you (you, as in your superuser +character) are actually moving around in the game creating and building rooms in sequence, just as +if you had been entering those commands manually, one by one. You have to take this into account +when creating the file, so that you can ‘walk’ (or teleport) to the right places in order.

      +

      This also means there are several pitfalls when designing and adding certain types of objects. Here +are some examples:

        -
      • Rooms that change your Command Set: Imagine that you build a ‘dark’ room, which severely limits the cmdsets of those entering it (maybe you have to find the light switch to proceed). In your batch script you would create this room, then teleport to it - and promptly be shifted into the dark state where none of your normal build commands work …

      • -
      • Auto-teleportation: Rooms that automatically teleport those that enter them to another place (like a trap room, for example). You would be teleported away too.

      • -
      • Mobiles: If you add aggressive mobs, they might attack you, drawing you into combat. If they have AI they might even follow you around when building - or they might move away from you before you’ve had time to finish describing and equipping them!

      • +
      • Rooms that change your Command Set: Imagine that you build a ‘dark’ room, which +severely limits the cmdsets of those entering it (maybe you have to find the light switch to +proceed). In your batch script you would create this room, then teleport to it - and promptly be +shifted into the dark state where none of your normal build commands work …

      • +
      • Auto-teleportation: Rooms that automatically teleport those that enter them to another place +(like a trap room, for example). You would be teleported away too.

      • +
      • Mobiles: If you add aggressive mobs, they might attack you, drawing you into combat. If they +have AI they might even follow you around when building - or they might move away from you before +you’ve had time to finish describing and equipping them!

      -

      The solution to all these is to plan ahead. Make sure that superusers are never affected by whatever effects are in play. Add an on/off switch to objects and make sure it’s always set to off upon creation. It’s all doable, one just needs to keep it in mind.

      +

      The solution to all these is to plan ahead. Make sure that superusers are never affected by whatever +effects are in play. Add an on/off switch to objects and make sure it’s always set to off upon +creation. It’s all doable, one just needs to keep it in mind.

      Assorted notes

      -

      The fact that you build as ‘yourself’ can also be considered an advantage however, should you ever decide to change the default command to allow others than superusers to call the processor. Since normal access-checks are still performed, a malevolent builder with access to the processor should not be able to do all that much damage (this is the main drawback of the Batch Code Processor)

      +

      The fact that you build as ‘yourself’ can also be considered an advantage however, should you ever +decide to change the default command to allow others than superusers to call the processor. Since +normal access-checks are still performed, a malevolent builder with access to the processor should +not be able to do all that much damage (this is the main drawback of the Batch Code +Processor)

        -
      • GNU Emacs users might find it interesting to use emacs’ evennia mode. This is an Emacs major mode found in evennia/utils/evennia-mode.el. It offers correct syntax highlighting and indentation with <tab> when editing .ev files in Emacs. See the header of that file for installation instructions.

      • -
      • VIM users can use amfl’s vim-evennia mode instead, see its readme for install instructions.

      • +
      • GNU Emacs users might find it interesting to use emacs’ +evennia mode. This is an Emacs major mode found in evennia/utils/evennia-mode.el. It offers +correct syntax highlighting and indentation with <tab> when editing .ev files in Emacs. See the +header of that file for installation instructions.

      • +
      • VIM users can use amfl’s vim-evennia +mode instead, see its readme for install instructions.

      @@ -246,7 +312,10 @@
    158. modules |
    159. - + + + + @@ -36,32 +41,75 @@

      Batch Processors

      -

      Building a game world is a lot of work, especially when starting out. Rooms should be created, descriptions have to be written, objects must be detailed and placed in their proper places. In many traditional MUD setups you had to do all this online, line by line, over a telnet session.

      -

      Evennia already moves away from much of this by shifting the main coding work to external Python modules. But also building would be helped if one could do some or all of it externally. Enter Evennia’s batch processors (there are two of them). The processors allows you, as a game admin, to build your game completely offline in normal text files (batch files) that the processors understands. Then, when you are ready, you use the processors to read it all into Evennia (and into the database) in one go.

      -

      You can of course still build completely online should you want to - this is certainly the easiest way to go when learning and for small build projects. But for major building work, the advantages of using the batch-processors are many:

      +

      Building a game world is a lot of work, especially when starting out. Rooms should be created, +descriptions have to be written, objects must be detailed and placed in their proper places. In many +traditional MUD setups you had to do all this online, line by line, over a telnet session.

      +

      Evennia already moves away from much of this by shifting the main coding work to external Python +modules. But also building would be helped if one could do some or all of it externally. Enter +Evennia’s batch processors (there are two of them). The processors allows you, as a game admin, to +build your game completely offline in normal text files (batch files) that the processors +understands. Then, when you are ready, you use the processors to read it all into Evennia (and into +the database) in one go.

      +

      You can of course still build completely online should you want to - this is certainly the easiest +way to go when learning and for small build projects. But for major building work, the advantages of +using the batch-processors are many:

        -
      • It’s hard to compete with the comfort of a modern desktop text editor; Compared to a traditional MUD line input, you can get much better overview and many more features. Also, accidentally pressing Return won’t immediately commit things to the database.

      • -
      • You might run external spell checkers on your batch files. In the case of one of the batch-processors (the one that deals with Python code), you could also run external debuggers and code analyzers on your file to catch problems before feeding it to Evennia.

      • -
      • The batch files (as long as you keep them) are records of your work. They make a natural starting point for quickly re-building your world should you ever decide to start over.

      • -
      • If you are an Evennia developer, using a batch file is a fast way to setup a test-game after having reset the database.

      • -
      • The batch files might come in useful should you ever decide to distribute all or part of your world to others.

      • +
      • It’s hard to compete with the comfort of a modern desktop text editor; Compared to a traditional +MUD line input, you can get much better overview and many more features. Also, accidentally pressing +Return won’t immediately commit things to the database.

      • +
      • You might run external spell checkers on your batch files. In the case of one of the batch- +processors (the one that deals with Python code), you could also run external debuggers and code +analyzers on your file to catch problems before feeding it to Evennia.

      • +
      • The batch files (as long as you keep them) are records of your work. They make a natural starting +point for quickly re-building your world should you ever decide to start over.

      • +
      • If you are an Evennia developer, using a batch file is a fast way to setup a test-game after +having reset the database.

      • +
      • The batch files might come in useful should you ever decide to distribute all or part of your +world to others.

      -

      There are two batch processors, the Batch-command processor and the Batch-code processor. The first one is the simpler of the two. It doesn’t require any programming knowledge - you basically just list in-game commands in a text file. The code-processor on the other hand is much more powerful but also more complex - it lets you use Evennia’s API to code your world in full-fledged Python code.

      +

      There are two batch processors, the Batch-command processor and the Batch-code processor. The +first one is the simpler of the two. It doesn’t require any programming knowledge - you basically +just list in-game commands in a text file. The code-processor on the other hand is much more +powerful but also more complex - it lets you use Evennia’s API to code your world in full-fledged +Python code.

      -

      If you plan to use international characters in your batchfiles you are wise to read about file encodings below.

      +

      If you plan to use international characters in your batchfiles you are wise to read about file +encodings below.

      A note on File Encodings

      -

      As mentioned, both the processors take text files as input and then proceed to process them. As long as you stick to the standard ASCII character set (which means the normal English characters, basically) you should not have to worry much about this section.

      -

      Many languages however use characters outside the simple ASCII table. Common examples are various apostrophes and umlauts but also completely different symbols like those of the greek or cyrillic alphabets.

      -

      First, we should make it clear that Evennia itself handles international characters just fine. It (and Django) uses unicode strings internally.

      -

      The problem is that when reading a text file like the batchfile, we need to know how to decode the byte-data stored therein to universal unicode. That means we need an encoding (a mapping) for how the file stores its data. There are many, many byte-encodings used around the world, with opaque names such as Latin-1, ISO-8859-3 or ARMSCII-8 to pick just a few examples. Problem is that it’s practially impossible to determine which encoding was used to save a file just by looking at it (it’s just a bunch of bytes!). You have to know.

      -

      With this little introduction it should be clear that Evennia can’t guess but has to assume an encoding when trying to load a batchfile. The text editor and Evennia must speak the same “language” so to speak. Evennia will by default first try the international UTF-8 encoding, but you can have Evennia try any sequence of different encodings by customizing the ENCODINGS list in your settings file. Evennia will use the first encoding in the list that do not raise any errors. Only if none work will the server give up and return an error message.

      -

      You can often change the text editor encoding (this depends on your editor though), otherwise you need to add the editor’s encoding to Evennia’s ENCODINGS list. If you are unsure, write a test file with lots of non-ASCII letters in the editor of your choice, then import to make sure it works as it should.

      -

      More help with encodings can be found in the entry Text Encodings and also in the Wikipedia article here.

      -

      A footnote for the batch-code processor: Just because Evennia can parse your file and your fancy special characters, doesn’t mean that Python allows their use. Python syntax only allows international characters inside strings. In all other source code only ASCII set characters are allowed.

      +

      As mentioned, both the processors take text files as input and then proceed to process them. As long +as you stick to the standard ASCII character set (which means +the normal English characters, basically) you should not have to worry much about this section.

      +

      Many languages however use characters outside the simple ASCII table. Common examples are various +apostrophes and umlauts but also completely different symbols like those of the greek or cyrillic +alphabets.

      +

      First, we should make it clear that Evennia itself handles international characters just fine. It +(and Django) uses unicode strings internally.

      +

      The problem is that when reading a text file like the batchfile, we need to know how to decode the +byte-data stored therein to universal unicode. That means we need an encoding (a mapping) for how +the file stores its data. There are many, many byte-encodings used around the world, with opaque +names such as Latin-1, ISO-8859-3 or ARMSCII-8 to pick just a few examples. Problem is that +it’s practially impossible to determine which encoding was used to save a file just by looking at it +(it’s just a bunch of bytes!). You have to know.

      +

      With this little introduction it should be clear that Evennia can’t guess but has to assume an +encoding when trying to load a batchfile. The text editor and Evennia must speak the same “language” +so to speak. Evennia will by default first try the international UTF-8 encoding, but you can have +Evennia try any sequence of different encodings by customizing the ENCODINGS list in your settings +file. Evennia will use the first encoding in the list that do not raise any errors. Only if none +work will the server give up and return an error message.

      +

      You can often change the text editor encoding (this depends on your editor though), otherwise you +need to add the editor’s encoding to Evennia’s ENCODINGS list. If you are unsure, write a test +file with lots of non-ASCII letters in the editor of your choice, then import to make sure it works +as it should.

      +

      More help with encodings can be found in the entry Text Encodings and also in the +Wikipedia article here.

      +

      A footnote for the batch-code processor: Just because Evennia can parse your file and your +fancy special characters, doesn’t mean that Python allows their use. Python syntax only allows +international characters inside strings. In all other source code only ASCII set characters are +allowed.

      @@ -118,7 +166,10 @@
    160. modules |
    161. - + + + + @@ -39,32 +44,53 @@

      What is Bootstrap?

      -

      Evennia’s new default web page uses a framework called Bootstrap. 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.

      +

      Evennia’s new default web page uses a framework called Bootstrap. 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.


      The Layout System

      -

      Other than the basic styling Bootstrap includes, it also includes a built in layout and grid system. -The first part of this system is the container.

      -

      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.

      +

      Other than the basic styling Bootstrap includes, it also includes a built in layout and grid +system. +The first part of this system is the +container.

      +

      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.

      <div class="container">
           <!--- Your content here -->
       </div>
       
      -

      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.

      +

      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.

      <div class="container-fluid">
           <!--- This content will span the whole page -->
       </div>
       
      -

      The second part of the layout system is the 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.

      +

      The second part of the layout system is the 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.

      +

      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.

      -

      To use the grid system, first create a container for your content, then add your rows and columns like so:

      +

      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.

      +

      To use the grid system, first create a container for your content, then add your rows and columns +like so:

      <div class="container">
           <div class="row">
               <div class="col">
      @@ -81,7 +107,8 @@ Fixed-width containers take up a certain max-width of the page - they’re usefu
       

      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!

      +

      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!

      <div class="container">
           <div class="row">
               <div class="col col-md-6 col-lg-3">
      @@ -100,13 +127,17 @@ Fixed-width containers take up a certain max-width of the page - they’re usefu
       </div>
       
      -

      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

      +

      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


      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 or read one of our other web tutorials.

      +

      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.

      @@ -165,7 +196,10 @@ Fixed-width containers take up a certain max-width of the page - they’re usefu
    162. modules |
    163. - + + + + @@ -36,21 +41,31 @@

      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.

      +

      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 or the web character view tutorial +

      Please take a look at either the basic web tutorial or the web +character view tutorial 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.

      +

      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 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.

      +

      Most elements can be styled with default colors. Take a look at the +documentation 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..

      +

      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..

      <span class="border border-dark"></span>
       
      @@ -61,7 +76,10 @@ to get a feel for how to add pages to Evennia’s website to test these examples

      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 to learn more.

      +

      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 to +learn more.


      @@ -69,7 +87,8 @@ to get a feel for how to add pages to Evennia’s website to test these examples

      Components

      Buttons

      -

      Buttons in Bootstrap are very easy to use - button styling can be added to <button>, <a>, and <input> elements.

      +

      Buttons in Bootstrap are very easy to use - +button styling can be added to <button>, <a>, and <input> elements.

      <a class="btn btn-primary" href="#" role="button">I'm a Button</a>
       <button class="btn btn-primary" type="submit">Me too!</button>
       <input class="btn btn-primary" type="button" value="Button">
      @@ -80,7 +99,10 @@ to get a feel for how to add pages to Evennia’s website to test these examples
       

      Cards

      -

      Cards 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.

      +

      Cards 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.

      <div class="card">
         <div class="card-body">
           <h4 class="card-title">Card title</h4>
      @@ -94,7 +116,9 @@ to get a feel for how to add pages to Evennia’s website to test these examples
       

      Jumbotron

      -

      Jumbotrons 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.

      +

      Jumbotrons 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.

      <div class="jumbotron jumbotron-fluid">
         <div class="container">
           <h1 class="display-3">Full Width Jumbotron</h1>
      @@ -106,7 +130,9 @@ to get a feel for how to add pages to Evennia’s website to test these examples
       

      Forms

      -

      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.

      +

      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.

      @@ -176,7 +202,10 @@ to get a feel for how to add pages to Evennia’s website to test these examples
    164. modules |
    165. - + + + +
      @@ -45,9 +50,9 @@
    166. Giving build permissions to others

    167. Adding text tags

    168. Customizing the connection screen

    169. @@ -129,7 +134,10 @@
    170. modules |
    171. - + + + +
      @@ -36,23 +41,45 @@

      Building Permissions

      -

      OBS: This gives only a brief introduction to the access system. Locks and permissions are fully detailed here.

      +

      OBS: This gives only a brief introduction to the access system. Locks and permissions are fully +detailed here.

      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.

      +

      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 is used. By default Evennia creates the following hierarchy (spelled exactly like this):

      +

      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 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.

      2. -
      3. 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.

      4. -
      5. Builders - have all the build commands, but cannot affect other accounts or mess with the server.

      6. +
      7. 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.

      8. +
      9. 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.

      10. +
      11. Builders - have all the build commands, but cannot affect other accounts or mess with the +server.

      12. Helpers are almost like a normal Player, but they can also add help files to the database.

      13. -
      14. 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.

      15. +
      16. 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

      +

      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
       
      @@ -60,15 +87,27 @@
      @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).

      +

      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:

      +

      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.

      +

      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.

      @@ -127,7 +166,10 @@
    172. modules |
    173. - + + + +
      @@ -49,29 +54,47 @@ your command (so entering
       command[/switch/switch...] [arguments ...]
       
      -

      A switch is a special, optional flag to the command to make it behave differently. It is always put directly after the command name, and begins with a forward slash (/). The arguments are one or more inputs to the commands. It’s common to use an equal sign (=) when assigning something to an object.

      -

      Below are some examples of commands you can try when logged in to the game. Use help <command> for learning more about each command and their detailed options.

      +

      A switch is a special, optional flag to the command to make it behave differently. It is always +put directly after the command name, and begins with a forward slash (/). The arguments are one +or more inputs to the commands. It’s common to use an equal sign (=) when assigning something to +an object.

      +

      Below are some examples of commands you can try when logged in to the game. Use help <command> for +learning more about each command and their detailed options.

      Stepping Down From Godhood

      -

      If you just installed Evennia, your very first player account is called user #1, also known as the superuser or god user. This user is very powerful, so powerful that it will override many game restrictions such as locks. This can be useful, but it also hides some functionality that you might want to test.

      +

      If you just installed Evennia, your very first player account is called user #1, also known as the +superuser or god user. This user is very powerful, so powerful that it will override many game +restrictions such as locks. This can be useful, but it also hides some functionality that you might +want to test.

      To temporarily step down from your superuser position you can use the quell command in-game:

      quell
       
      -

      This will make you start using the permission of your current character’s level instead of your superuser level. If you didn’t change any settings your game Character should have an Developer level permission - high as can be without bypassing locks like the superuser does. This will work fine for the examples on this page. Use unquell to get back to superuser status again afterwards.

      +

      This will make you start using the permission of your current character’s level instead of your +superuser level. If you didn’t change any settings your game Character should have an Developer +level permission - high as can be without bypassing locks like the superuser does. This will work +fine for the examples on this page. Use unquell to get back to superuser status again afterwards.

      Creating an Object

      -

      Basic objects can be anything – swords, flowers and non-player characters. They are created using the create command:

      +

      Basic objects can be anything – swords, flowers and non-player characters. They are created using +the create command:

      create box
       
      -

      This created a new ‘box’ (of the default object type) in your inventory. Use the command inventory (or i) to see it. Now, ‘box’ is a rather short name, let’s rename it and tack on a few aliases.

      +

      This created a new ‘box’ (of the default object type) in your inventory. Use the command inventory +(or i) to see it. Now, ‘box’ is a rather short name, let’s rename it and tack on a few aliases.

      name box = very large box;box;very;crate
       
      -

      We now renamed the box to very large box (and this is what we will see when looking at it), but we will also recognize it by any of the other names we give - like crate or simply box as before. We could have given these aliases directly after the name in the create command, this is true for all creation commands - you can always tag on a list of ;-separated aliases to the name of your new object. If you had wanted to not change the name itself, but to only add aliases, you could have used the alias command.

      -

      We are currently carrying the box. Let’s drop it (there is also a short cut to create and drop in one go by using the /drop switch, for example create/drop box).

      +

      We now renamed the box to very large box (and this is what we will see when looking at it), but we +will also recognize it by any of the other names we give - like crate or simply box as before. +We could have given these aliases directly after the name in the create command, this is true for +all creation commands - you can always tag on a list of ;-separated aliases to the name of your +new object. If you had wanted to not change the name itself, but to only add aliases, you could have +used the alias command.

      +

      We are currently carrying the box. Let’s drop it (there is also a short cut to create and drop in +one go by using the /drop switch, for example create/drop box).

      drop box 
       
      @@ -79,7 +102,8 @@ your command (so entering
      examine box
       
      -

      This will show some technical details about the box object. For now we will ignore what this information means.

      +

      This will show some technical details about the box object. For now we will ignore what this +information means.

      Try to look at the box to see the (default) description.

      look box
       You see nothing special.
      @@ -89,73 +113,120 @@ your command (so entering 
      describe box = This is a large and very heavy box.
       
      -

      If you try the get command we will pick up the box. So far so good, but if we really want this to be a large and heavy box, people should not be able to run off with it that easily. To prevent this we need to lock it down. This is done by assigning a Lock to it. Make sure the box was dropped in the room, then try this:

      +

      If you try the get command we will pick up the box. So far so good, but if we really want this to +be a large and heavy box, people should not be able to run off with it that easily. To prevent +this we need to lock it down. This is done by assigning a Lock to it. Make sure the box was +dropped in the room, then try this:

      lock box = get:false()
       
      -

      Locks represent a rather big topic, but for now that will do what we want. This will lock the box so noone can lift it. The exception is superusers, they override all locks and will pick it up anyway. Make sure you are quelling your superuser powers and try to get the box now:

      +

      Locks represent a rather big topic, but for now that will do what we want. This will lock +the box so noone can lift it. The exception is superusers, they override all locks and will pick it +up anyway. Make sure you are quelling your superuser powers and try to get the box now:

      > get box
       You can't get that.
       
      -

      Think thís default error message looks dull? The get command looks for an Attribute named get_err_msg for returning a nicer error message (we just happen to know this, you would need to peek into the code for the get command to find out.). You set attributes using the set command:

      +

      Think thís default error message looks dull? The get command looks for an Attribute +named get_err_msg for returning a nicer error message (we just happen to know this, you would need +to peek into the +code for +the get command to find out.). You set attributes using the set command:

      set box/get_err_msg = It's way too heavy for you to lift. 
       
      -

      Try to get it now and you should see a nicer error message echoed back to you. To see what this message string is in the future, you can use ‘examine.’

      +

      Try to get it now and you should see a nicer error message echoed back to you. To see what this +message string is in the future, you can use ‘examine.’

      examine box/get_err_msg
       
      -

      Examine will return the value of attributes, including color codes. examine here/desc would return the raw description of your current room (including color codes), so that you can copy-and-paste to set its description to something else.

      -

      You create new Commands (or modify existing ones) in Python outside the game. See the Adding Commands tutorial for help with creating your first own Command.

      +

      Examine will return the value of attributes, including color codes. examine here/desc would return +the raw description of your current room (including color codes), so that you can copy-and-paste to +set its description to something else.

      +

      You create new Commands (or modify existing ones) in Python outside the game. See the Adding +Commands tutorial for help with creating your first own Command.

      Get a Personality

      -

      Scripts are powerful out-of-character objects useful for many “under the hood” things. One of their optional abilities is to do things on a timer. To try out a first script, let’s put one on ourselves. There is an example script in evennia/contrib/tutorial_examples/bodyfunctions.py that is called BodyFunctions. To add this to us we will use the script command:

      +

      Scripts are powerful out-of-character objects useful for many “under the hood” things. +One of their optional abilities is to do things on a timer. To try out a first script, let’s put one +on ourselves. There is an example script in evennia/contrib/tutorial_examples/bodyfunctions.py +that is called BodyFunctions. To add this to us we will use the script command:

      script self = tutorial_examples.bodyfunctions.BodyFunctions
       
      -

      (note that you don’t have to give the full path as long as you are pointing to a place inside the contrib directory, it’s one of the places Evennia looks for Scripts). Wait a while and you will notice yourself starting making random observations.

      +

      (note that you don’t have to give the full path as long as you are pointing to a place inside the +contrib directory, it’s one of the places Evennia looks for Scripts). Wait a while and you will +notice yourself starting making random observations.

      script self 
       
      -

      This will show details about scripts on yourself (also examine works). You will see how long it is until it “fires” next. Don’t be alarmed if nothing happens when the countdown reaches zero - this particular script has a randomizer to determine if it will say something or not. So you will not see output every time it fires.

      +

      This will show details about scripts on yourself (also examine works). You will see how long it is +until it “fires” next. Don’t be alarmed if nothing happens when the countdown reaches zero - this +particular script has a randomizer to determine if it will say something or not. So you will not see +output every time it fires.

      When you are tired of your character’s “insights”, kill the script with

      script/stop self = tutorial_examples.bodyfunctions.BodyFunctions
       
      -

      You create your own scripts in Python, outside the game; the path you give to script is literally the Python path to your script file. The Scripts page explains more details.

      +

      You create your own scripts in Python, outside the game; the path you give to script is literally +the Python path to your script file. The Scripts page explains more details.

      Pushing Your Buttons

      -

      If we get back to the box we made, there is only so much fun you can do with it at this point. It’s just a dumb generic object. If you renamed it to stone and changed its description noone would be the wiser. However, with the combined use of custom Typeclasses, Scripts and object-based Commands, you could expand it and other items to be as unique, complex and interactive as you want.

      -

      Let’s take an example. So far we have only created objects that use the default object typeclass named simply Object. Let’s create an object that is a little more interesting. Under evennia/contrib/tutorial_examples there is a module red_button.py. It contains the enigmatic RedButton typeclass.

      +

      If we get back to the box we made, there is only so much fun you can do with it at this point. It’s +just a dumb generic object. If you renamed it to stone and changed its description noone would be +the wiser. However, with the combined use of custom Typeclasses, Scripts +and object-based Commands, you could expand it and other items to be as unique, complex +and interactive as you want.

      +

      Let’s take an example. So far we have only created objects that use the default object typeclass +named simply Object. Let’s create an object that is a little more interesting. Under +evennia/contrib/tutorial_examples there is a module red_button.py. It contains the enigmatic +RedButton typeclass.

      Let’s make us one of those!

      create/drop button:tutorial_examples.red_button.RedButton
       
      -

      We import the RedButton python class the same way you would import it in Python except Evennia makes sure to look inevennia/contrib/ so you don’t have to write the full path every time. There you go - one red button.

      -

      The RedButton is an example object intended to show off a few of Evennia’s features. You will find that the Typeclass and Commands controlling it are inside evennia/contrib/tutorial_examples/.

      -

      If you wait for a while (make sure you dropped it!) the button will blink invitingly. Why don’t you try to push it …? Surely a big red button is meant to be pushed. You know you want to.

      +

      We import the RedButton python class the same way you would import it in Python except Evennia makes +sure to look inevennia/contrib/ so you don’t have to write the full path every time. There you go

      +
        +
      • one red button.

      • +
      +

      The RedButton is an example object intended to show off a few of Evennia’s features. You will find +that the Typeclass and Commands controlling it are inside +evennia/contrib/tutorial_examples/.

      +

      If you wait for a while (make sure you dropped it!) the button will blink invitingly. Why don’t you +try to push it …? Surely a big red button is meant to be pushed. You know you want to.

      Making Yourself a House

      -

      The main command for shaping the game world is dig. For example, if you are standing in Limbo you can dig a route to your new house location like this:

      +

      The main command for shaping the game world is dig. For example, if you are standing in Limbo you +can dig a route to your new house location like this:

      dig house = large red door;door;in,to the outside;out
       
      -

      This will create a new room named ‘house’. Spaces at the start/end of names and aliases are ignored so you could put more air if you wanted. This call will directly create an exit from your current location named ‘large red door’ and a corresponding exit named ‘to the outside’ in the house room leading back to Limbo. We also define a few aliases to those exits, so people don’t have to write the full thing all the time.

      -

      If you wanted to use normal compass directions (north, west, southwest etc), you could do that with dig too. But Evennia also has a limited version of dig that helps for compass directions (and also up/down and in/out). It’s called tunnel:

      +

      This will create a new room named ‘house’. Spaces at the start/end of names and aliases are ignored +so you could put more air if you wanted. This call will directly create an exit from your current +location named ‘large red door’ and a corresponding exit named ‘to the outside’ in the house room +leading back to Limbo. We also define a few aliases to those exits, so people don’t have to write +the full thing all the time.

      +

      If you wanted to use normal compass directions (north, west, southwest etc), you could do that with +dig too. But Evennia also has a limited version of dig that helps for compass directions (and +also up/down and in/out). It’s called tunnel:

      tunnel sw = cliff
       
      -

      This will create a new room “cliff” with an exit “southwest” leading there and a path “northeast” leading back from the cliff to your current location.

      +

      This will create a new room “cliff” with an exit “southwest” leading there and a path “northeast” +leading back from the cliff to your current location.

      You can create new exits from where you are using the open command:

      open north;n = house
       

      This opens an exit north (with an alias n) to the previously created room house.

      -

      If you have many rooms named house you will get a list of matches and have to select which one you want to link to. You can also give its database (#dbref) number, which is unique to every object. This can be found with the examine command or by looking at the latest constructions with objects.

      +

      If you have many rooms named house you will get a list of matches and have to select which one you +want to link to. You can also give its database (#dbref) number, which is unique to every object. +This can be found with the examine command or by looking at the latest constructions with +objects.

      Follow the north exit to your ‘house’ or teleport to it:

      north
       
      @@ -172,7 +243,8 @@ your command (so entering

      Reshuffling the World

      -

      You can find things using the find command. Assuming you are back at Limbo, let’s teleport the large box to our house.

      +

      You can find things using the find command. Assuming you are back at Limbo, let’s teleport the +large box to our house.

      > teleport box = house
       very large box is leaving Limbo, heading for house.
       Teleported very large box -> house.
      @@ -184,34 +256,46 @@ your command (so entering very large box(#8) - src.objects.objects.Object
       
      -

      Knowing the #dbref of the box (#8 in this example), you can grab the box and get it back here without actually yourself going to house first:

      +

      Knowing the #dbref of the box (#8 in this example), you can grab the box and get it back here +without actually yourself going to house first:

      teleport #8 = here
       
      -

      (You can usually use here to refer to your current location. To refer to yourself you can use self or me). The box should now be back in Limbo with you.

      +

      (You can usually use here to refer to your current location. To refer to yourself you can use +self or me). The box should now be back in Limbo with you.

      We are getting tired of the box. Let’s destroy it.

      destroy box
       
      -

      You can destroy many objects in one go by giving a comma-separated list of objects (or their #dbrefs, if they are not in the same location) to the command.

      +

      You can destroy many objects in one go by giving a comma-separated list of objects (or their +#dbrefs, if they are not in the same location) to the command.

      Adding a Help Entry

      -

      An important part of building is keeping the help files updated. You can add, delete and append to existing help entries using the sethelp command.

      +

      An important part of building is keeping the help files updated. You can add, delete and append to +existing help entries using the sethelp command.

      sethelp/add MyTopic = This help topic is about ... 
       

      Adding a World

      -

      After this brief introduction to building you may be ready to see a more fleshed-out example. Evennia comes with a tutorial world for you to explore.

      -

      First you need to switch back to superuser by using the unquell command. Next, place yourself in Limbo and run the following command:

      +

      After this brief introduction to building you may be ready to see a more fleshed-out example. +Evennia comes with a tutorial world for you to explore.

      +

      First you need to switch back to superuser by using the unquell command. Next, place yourself in +Limbo and run the following command:

      batchcommand tutorial_world.build
       
      -

      This will take a while (be patient and don’t re-run the command). You will see all the commands used to build the world scroll by as the world is built for you.

      -

      You will end up with a new exit from Limbo named tutorial. Apart from being a little solo-adventure in its own right, the tutorial world is a good source for learning Evennia building (and coding).

      -

      Read the batch file to see exactly how it’s built, step by step. See also more info about the tutorial world here.

      +

      This will take a while (be patient and don’t re-run the command). You will see all the commands used +to build the world scroll by as the world is built for you.

      +

      You will end up with a new exit from Limbo named tutorial. Apart from being a little solo- +adventure in its own right, the tutorial world is a good source for learning Evennia building (and +coding).

      +

      Read the batch +file to see +exactly how it’s built, step by step. See also more info about the tutorial world [here](Tutorial- +World-Introduction).

      @@ -275,7 +359,10 @@ your command (so entering modules | - + + + +
      @@ -37,15 +42,19 @@

      Building a mech tutorial

      -

      This page was adapted from the article “Building a Giant Mech in Evennia” by Griatch, published in Imaginary Realities Volume 6, issue 1, 2014. The original article is no longer available online, this is a version adopted to be compatible with the latest Evennia.

      +

      This page was adapted from the article “Building a Giant Mech in Evennia” by Griatch, published in +Imaginary Realities Volume 6, issue 1, 2014. The original article is no longer available online, +this is a version adopted to be compatible with the latest Evennia.

      Creating the Mech

      -

      Let us create a functioning giant mech using the Python MUD-creation system Evennia. Everyone likes a giant mech, right? Start in-game as a character with build privileges (or the superuser).

      +

      Let us create a functioning giant mech using the Python MUD-creation system Evennia. Everyone likes +a giant mech, right? Start in-game as a character with build privileges (or the superuser).

      @create/drop Giant Mech ; mech
       
      -

      Boom. We created a Giant Mech Object and dropped it in the room. We also gave it an alias mech. Let’s describe it.

      +

      Boom. We created a Giant Mech Object and dropped it in the room. We also gave it an alias mech. +Let’s describe it.

      @desc mech = This is a huge mech. It has missiles and stuff.
       
      @@ -53,37 +62,52 @@
      @lock mech = puppet:all()
       
      -

      This makes it so that everyone can control the mech. More mechs to the people! (Note that whereas Evennia’s default commands may look vaguely MUX-like, you can change the syntax to look like whatever interface style you prefer.)

      -

      Before we continue, let’s make a brief detour. Evennia is very flexible about its objects and even more flexible about using and adding commands to those objects. Here are some ground rules well worth remembering for the remainder of this article:

      +

      This makes it so that everyone can control the mech. More mechs to the people! (Note that whereas +Evennia’s default commands may look vaguely MUX-like, you can change the syntax to look like +whatever interface style you prefer.)

      +

      Before we continue, let’s make a brief detour. Evennia is very flexible about its objects and even +more flexible about using and adding commands to those objects. Here are some ground rules well +worth remembering for the remainder of this article:

      • The Account represents the real person logging in and has no game-world existence.

      • Any Object can be puppeted by an Account (with proper permissions).

      • -
      • Characters, Rooms, and Exits are just children of normal Objects.

      • +
      • Characters, Rooms, and Exits are just +children of normal Objects.

      • Any Object can be inside another (except if it creates a loop).

      • Any Object can store custom sets of commands on it. Those commands can:

        • be made available to the puppeteer (Account),

        • be made available to anyone in the same location as the Object, and

        • be made available to anyone “inside” the Object

        • -
        • Also Accounts can store commands on themselves. Account commands are always available unless commands on a puppeted Object explicitly override them.

        • +
        • Also Accounts can store commands on themselves. Account commands are always available unless +commands on a puppeted Object explicitly override them.

      -

      In Evennia, using the @ic command will allow you to puppet a given Object (assuming you have puppet-access to do so). As mentioned above, the bog-standard Character class is in fact like any Object: it is auto-puppeted when logging in and just has a command set on it containing the normal in-game commands, like look, inventory, get and so on.

      +

      In Evennia, using the @ic command will allow you to puppet a given Object (assuming you have +puppet-access to do so). As mentioned above, the bog-standard Character class is in fact like any +Object: it is auto-puppeted when logging in and just has a command set on it containing the normal +in-game commands, like look, inventory, get and so on.

      @ic mech
       
      -

      You just jumped out of your Character and are now the mech! If people look at you in-game, they will look at a mech. The problem at this point is that the mech Object has no commands of its own. The usual things like look, inventory and get sat on the Character object, remember? So at the moment the mech is not quite as cool as it could be.

      +

      You just jumped out of your Character and are now the mech! If people look at you in-game, they +will look at a mech. The problem at this point is that the mech Object has no commands of its own. +The usual things like look, inventory and get sat on the Character object, remember? So at the +moment the mech is not quite as cool as it could be.

      @ic <Your old Character>
       

      You just jumped back to puppeting your normal, mundane Character again. All is well.

      -

      (But, you ask, where did that @ic command come from, if the mech had no commands on it? The answer is that it came from the Account’s command set. This is important. Without the Account being the one with the @ic command, we would not have been able to get back out of our mech again.)

      +

      (But, you ask, where did that @ic command come from, if the mech had no commands on it? The +answer is that it came from the Account’s command set. This is important. Without the Account being +the one with the @ic command, we would not have been able to get back out of our mech again.)

      Arming the Mech

      -

      Let us make the mech a little more interesting. In our favorite text editor, we will create some new mech-suitable commands. In Evennia, commands are defined as Python classes.

      +

      Let us make the mech a little more interesting. In our favorite text editor, we will create some new +mech-suitable commands. In Evennia, commands are defined as Python classes.

       1
        2
        3
      @@ -161,13 +185,18 @@
           # (it's very similar to the 'shoot' command above).
       
      -

      This is saved as a normal Python module (let’s call it mechcommands.py), in a place Evennia looks for such modules (mygame/commands/). This command will trigger when the player gives the command “shoot”, “fire,” or even “fire!” with an exclamation mark. The mech can shoot in the air or at a target if you give one. In a real game the gun would probably be given a chance to hit and give damage to the target, but this is enough for now.

      +

      This is saved as a normal Python module (let’s call it mechcommands.py), in a place Evennia looks +for such modules (mygame/commands/). This command will trigger when the player gives the command +“shoot”, “fire,” or even “fire!” with an exclamation mark. The mech can shoot in the air or at a +target if you give one. In a real game the gun would probably be given a chance to hit and give +damage to the target, but this is enough for now.

      We also make a second command for launching missiles (CmdLaunch). To save space we won’t describe it here; it looks the same except it returns a text about the missiles being fired and has different key and aliases. We leave that up to you to create as an exercise. You could have it print “WOOSH! The mech launches missiles against !”, for example.

      -

      Now we shove our commands into a command set. A Command Set (CmdSet) is a container holding any number of commands. The command set is what we will store on the mech.

      +

      Now we shove our commands into a command set. A Command Set (CmdSet) is a container +holding any number of commands. The command set is what we will store on the mech.

       1
        2
        3
      @@ -199,11 +228,14 @@ mech launches missiles against !”, for example.

      self.add(CmdLaunch())
      -

      This simply groups all the commands we want. We add our new shoot/launch commands. Let’s head back into the game. For testing we will manually attach our new CmdSet to the mech.

      +

      This simply groups all the commands we want. We add our new shoot/launch commands. Let’s head back +into the game. For testing we will manually attach our new CmdSet to the mech.

      @py self.search("mech").cmdset.add("commands.mechcommands.MechCmdSet")
       
      -

      This is a little Python snippet (run from the command line as an admin) that searches for the mech in our current location and attaches our new MechCmdSet to it. What we add is actually the Python path to our cmdset class. Evennia will import and initialize it behind the scenes.

      +

      This is a little Python snippet (run from the command line as an admin) that searches for the mech +in our current location and attaches our new MechCmdSet to it. What we add is actually the Python +path to our cmdset class. Evennia will import and initialize it behind the scenes.

      @ic mech
       
      @@ -212,16 +244,25 @@ mech launches missiles against !”, for example.

      BOOM! The mech fires its gun in the air!
      -

      There we go, one functioning mech. Try your own launch command and see that it works too. We can not only walk around as the mech — since the CharacterCmdSet is included in our MechCmdSet, the mech can also do everything a Character could do, like look around, pick up stuff, and have an inventory. We could now shoot the gun at a target or try the missile launch command. Once you have your own mech, what else do you need?

      +

      There we go, one functioning mech. Try your own launch command and see that it works too. We can +not only walk around as the mech — since the CharacterCmdSet is included in our MechCmdSet, the mech +can also do everything a Character could do, like look around, pick up stuff, and have an inventory. +We could now shoot the gun at a target or try the missile launch command. Once you have your own +mech, what else do you need?

      -

      Note: You’ll find that the mech’s commands are available to you by just standing in the same location (not just by puppeting it). We’ll solve this with a lock in the next section.

      +

      Note: You’ll find that the mech’s commands are available to you by just standing in the same +location (not just by puppeting it). We’ll solve this with a lock in the next section.

      Making a Mech production line

      -

      What we’ve done so far is just to make a normal Object, describe it and put some commands on it. This is great for testing. The way we added it, the MechCmdSet will even go away if we reload the server. Now we want to make the mech an actual object “type” so we can create mechs without those extra steps. For this we need to create a new Typeclass.

      -

      A Typeclass is a near-normal Python class that stores its existence to the database behind the scenes. A Typeclass is created in a normal Python source file:

      +

      What we’ve done so far is just to make a normal Object, describe it and put some commands on it. +This is great for testing. The way we added it, the MechCmdSet will even go away if we reload the +server. Now we want to make the mech an actual object “type” so we can create mechs without those +extra steps. For this we need to create a new Typeclass.

      +

      A Typeclass is a near-normal Python class that stores its existence to the database +behind the scenes. A Typeclass is created in a normal Python source file:

       1
        2
        3
      @@ -255,13 +296,22 @@ BOOM! The mech fires its gun in the air!
               self.db.desc = "This is a huge mech. It has missiles and stuff."
       
      -

      For convenience we include the full contents of the default CharacterCmdSet in there. This will make a Character’s normal commands available to the mech. We also add the mech-commands from before, making sure they are stored persistently in the database. The locks specify that anyone can puppet the meck and no-one can “call” the mech’s Commands from ‘outside’ it - you have to puppet it to be able to shoot.

      -

      That’s it. When Objects of this type are created, they will always start out with the mech’s command set and the correct lock. We set a default description, but you would probably change this with @desc to individualize your mechs as you build them.

      +

      For convenience we include the full contents of the default CharacterCmdSet in there. This will +make a Character’s normal commands available to the mech. We also add the mech-commands from before, +making sure they are stored persistently in the database. The locks specify that anyone can puppet +the meck and no-one can “call” the mech’s Commands from ‘outside’ it - you have to puppet it to be +able to shoot.

      +

      That’s it. When Objects of this type are created, they will always start out with the mech’s command +set and the correct lock. We set a default description, but you would probably change this with +@desc to individualize your mechs as you build them.

      Back in the game, just exit the old mech (@ic back to your old character) then do

      @create/drop The Bigger Mech ; bigmech : mech.Mech
       
      -

      We create a new, bigger mech with an alias bigmech. Note how we give the python-path to our Typeclass at the end — this tells Evennia to create the new object based on that class (we don’t have to give the full path in our game dir typeclasses.mech.Mech because Evennia knows to look in the typeclasses folder already). A shining new mech will appear in the room! Just use

      +

      We create a new, bigger mech with an alias bigmech. Note how we give the python-path to our +Typeclass at the end — this tells Evennia to create the new object based on that class (we don’t +have to give the full path in our game dir typeclasses.mech.Mech because Evennia knows to look in +the typeclasses folder already). A shining new mech will appear in the room! Just use

      @ic bigmech
       
      @@ -269,9 +319,16 @@ BOOM! The mech fires its gun in the air!

      Future Mechs

      -

      To expand on this you could add more commands to the mech and remove others. Maybe the mech shouldn’t work just like a Character after all. Maybe it makes loud noises every time it passes from room to room. Maybe it cannot pick up things without crushing them. Maybe it needs fuel, ammo and repairs. Maybe you’ll lock it down so it can only be puppeted by emo teenagers.

      -

      Having you puppet the mech-object directly is also just one way to implement a giant mech in Evennia.

      -

      For example, you could instead picture a mech as a “vehicle” that you “enter” as your normal Character (since any Object can move inside another). In that case the “insides” of the mech Object could be the “cockpit”. The cockpit would have the MechCommandSet stored on itself and all the shooting goodness would be made available to you only when you enter it.

      +

      To expand on this you could add more commands to the mech and remove others. Maybe the mech +shouldn’t work just like a Character after all. Maybe it makes loud noises every time it passes from +room to room. Maybe it cannot pick up things without crushing them. Maybe it needs fuel, ammo and +repairs. Maybe you’ll lock it down so it can only be puppeted by emo teenagers.

      +

      Having you puppet the mech-object directly is also just one way to implement a giant mech in +Evennia.

      +

      For example, you could instead picture a mech as a “vehicle” that you “enter” as your normal +Character (since any Object can move inside another). In that case the “insides” of the mech Object +could be the “cockpit”. The cockpit would have the MechCommandSet stored on itself and all the +shooting goodness would be made available to you only when you enter it.

      And of course you could put more guns on it. And make it fly.

      @@ -334,7 +391,10 @@ BOOM! The mech fires its gun in the air!
    174. modules |
    175. - + + + + @@ -39,20 +44,33 @@

      The building_menu contrib

      -

      This contrib allows you to write custom and easy to use building menus. As the name implies, these menus are most useful for building things, that is, your builders might appreciate them, although you can use them for your players as well.

      -

      Building menus are somewhat similar to EvMenu although they don’t use the same system at all and are intended to make building easier. They replicate what other engines refer to as “building editors”, which allow to you to build in a menu instead of having to enter a lot of complex commands. Builders might appreciate this simplicity, and if the code that was used to create them is simple as well, coders could find this contrib useful.

      +

      This contrib allows you to write custom and easy to use building menus. As the name implies, these +menus are most useful for building things, that is, your builders might appreciate them, although +you can use them for your players as well.

      +

      Building menus are somewhat similar to EvMenu although they don’t use the same system at all and +are intended to make building easier. They replicate what other engines refer to as “building +editors”, which allow to you to build in a menu instead of having to enter a lot of complex +commands. Builders might appreciate this simplicity, and if the code that was used to create them +is simple as well, coders could find this contrib useful.

      A simple menu

      Before diving in, there are some things to point out:

        -
      • Building menus work on an object. This object will be edited by manipulations in the menu. So you can create a menu to add/edit a room, an exit, a character and so on.

      • -
      • Building menus are arranged in layers of choices. A choice gives access to an option or to a sub-menu. Choices are linked to commands (usually very short). For instance, in the example shown below, to edit the room key, after opening the building menu, you can type k. That will lead you to the key choice where you can enter a new key for the room. Then you can enter @ to leave this choice and go back to the entire menu. (All of this can be changed).

      • -
      • To open the menu, you will need something like a command. This contrib offers a basic command for demonstration, but we will override it in this example, using the same code with more flexibility.

      • +
      • Building menus work on an object. This object will be edited by manipulations in the menu. So +you can create a menu to add/edit a room, an exit, a character and so on.

      • +
      • Building menus are arranged in layers of choices. A choice gives access to an option or to a sub- +menu. Choices are linked to commands (usually very short). For instance, in the example shown +below, to edit the room key, after opening the building menu, you can type k. That will lead you +to the key choice where you can enter a new key for the room. Then you can enter @ to leave this +choice and go back to the entire menu. (All of this can be changed).

      • +
      • To open the menu, you will need something like a command. This contrib offers a basic command for +demonstration, but we will override it in this example, using the same code with more flexibility.

      So let’s add a very basic example to begin with.

      A generic editing command

      -

      Let’s begin by adding a new command. You could add or edit the following file (there’s no trick here, feel free to organize the code differently):

      +

      Let’s begin by adding a new command. You could add or edit the following file (there’s no trick +here, feel free to organize the code differently):

       1
        2
        3
      @@ -95,7 +113,8 @@
       40
       41
       42
      -43
      # file: commands/building.py
      +43
      +44
      # file: commands/building.py
       from evennia.contrib.building_menu import BuildingMenu
       from commands.command import Command
       
      @@ -133,7 +152,8 @@
               if obj.typename == "Room":
                   Menu = RoomBuildingMenu
               else:
      -            self.msg("|rThe object {} cannot be edited.|n".format(obj.get_display_name(self.caller)))
      +            self.msg("|rThe object {} cannot be
      +edited.|n".format(obj.get_display_name(self.caller)))
                   return
       
               menu = Menu(self.caller, obj)
      @@ -143,18 +163,28 @@
       

      This command is rather simple in itself:

      1. It has a key @edit and a lock to only allow builders to use it.

      2. -
      3. In its func method, it begins by checking the arguments, returning an error if no argument is specified.

      4. -
      5. It then searches for the given argument. We search globally. The search method used in this way will return the found object or None. It will also send the error message to the caller if necessary.

      6. -
      7. Assuming we have found an object, we check the object typename. This will be used later when we want to display several building menus. For the time being, we only handle Room. If the caller specified something else, we’ll display an error.

      8. -
      9. Assuming this object is a Room, we have defined a Menu object containing the class of our building menu. We build this class (creating an instance), giving it the caller and the object to edit.

      10. +
      11. In its func method, it begins by checking the arguments, returning an error if no argument is +specified.

      12. +
      13. It then searches for the given argument. We search globally. The search method used in this +way will return the found object or None. It will also send the error message to the caller if +necessary.

      14. +
      15. Assuming we have found an object, we check the object typename. This will be used later when +we want to display several building menus. For the time being, we only handle Room. If the +caller specified something else, we’ll display an error.

      16. +
      17. Assuming this object is a Room, we have defined a Menu object containing the class of our +building menu. We build this class (creating an instance), giving it the caller and the object to +edit.

      18. We then open the building menu, using the open method.

      -

      The end might sound a bit surprising at first glance. But the process is still very simple: we create an instance of our building menu and call its open method. Nothing more.

      +

      The end might sound a bit surprising at first glance. But the process is still very simple: we +create an instance of our building menu and call its open method. Nothing more.

      Where is our building menu?

      -

      If you go ahead and add this command and test it, you’ll get an error. We haven’t defined RoomBuildingMenu yet.

      -

      To add this command, edit commands/default_cmdsets.py. Import our command, adding an import line at the top of the file:

      +

      If you go ahead and add this command and test it, you’ll get an error. We haven’t defined +RoomBuildingMenu yet.

      +

      To add this command, edit commands/default_cmdsets.py. Import our command, adding an import line +at the top of the file:

      1
       2
       3
      @@ -211,7 +241,8 @@
       

      Our first menu

      -

      So far, we can’t use our building menu. Our @edit command will throw an error. We have to define the RoomBuildingMenu class. Open the commands/building.py file and add to the end of the file:

      +

      So far, we can’t use our building menu. Our @edit command will throw an error. We have to define +the RoomBuildingMenu class. Open the commands/building.py file and add to the end of the file:

       1
        2
        3
      @@ -241,7 +272,9 @@
               self.add_choice("key", "k", attr="key")
       
      -

      Save these changes, reload your game. You can now use the @edit command. Here’s what we get (notice that the commands we enter into the game are prefixed with > , though this prefix will probably not appear in your MUD client):

      +

      Save these changes, reload your game. You can now use the @edit command. Here’s what we get +(notice that the commands we enter into the game are prefixed with > , though this prefix will +probably not appear in your MUD client):

      > look
       Limbo(#2)
       Welcome to your new Evennia-based game! Visit http://www.evennia.com if you need
      @@ -306,12 +339,14 @@ As Account #1 you can create a demo/tutorial area with @batchcommand tutorial_wo
       
    176. When we use the @edit here command, a building menu for this room appears.

    177. This menu has two choices:

        -
      • Enter k to edit the room key. You will go into a choice where you can simply type the key room key (the way we have done here). You can use @ to go back to the menu.

      • +
      • Enter k to edit the room key. You will go into a choice where you can simply type the key +room key (the way we have done here). You can use @ to go back to the menu.

      • You can use q to quit the menu.

    178. -

      We then check, with the look command, that the menu has modified this room key. So by adding a class, with a method and a single line of code within, we’ve added a menu with two choices.

      +

      We then check, with the look command, that the menu has modified this room key. So by adding a +class, with a method and a single line of code within, we’ve added a menu with two choices.

      Code explanation

      @@ -340,40 +375,59 @@ As Account #1 you can create a demo/tutorial area with @batchcommand tutorial_wo
        -
      • We first create a class inheriting from BuildingMenu. This is usually the case when we want to create a building menu with this contrib.

      • +
      • We first create a class inheriting from BuildingMenu. This is usually the case when we want to +create a building menu with this contrib.

      • In this class, we override the init method, which is called when the menu opens.

      • -
      • In this init method, we call add_choice. This takes several arguments, but we’ve defined only three here:

        +
      • In this init method, we call add_choice. This takes several arguments, but we’ve defined only +three here:

          -
        • The choice name. This is mandatory and will be used by the building menu to know how to display this choice.

        • -
        • The command key to access this choice. We’ve given a simple "k". Menu commands usually are pretty short (that’s part of the reason building menus are appreciated by builders). You can also specify additional aliases, but we’ll see that later.

        • -
        • We’ve added a keyword argument, attr. This tells the building menu that when we are in this choice, the text we enter goes into this attribute name. It’s called attr, but it could be a room attribute or a typeclass persistent or non-persistent attribute (we’ll see other examples as well).

        • +
        • The choice name. This is mandatory and will be used by the building menu to know how to +display this choice.

        • +
        • The command key to access this choice. We’ve given a simple "k". Menu commands usually are +pretty short (that’s part of the reason building menus are appreciated by builders). You can also +specify additional aliases, but we’ll see that later.

        • +
        • We’ve added a keyword argument, attr. This tells the building menu that when we are in this +choice, the text we enter goes into this attribute name. It’s called attr, but it could be a room +attribute or a typeclass persistent or non-persistent attribute (we’ll see other examples as well).

      We’ve added the menu choice for key here, why is another menu choice defined for quit?

      -

      Our building menu creates a choice at the end of our choice list if it’s a top-level menu (sub-menus don’t have this feature). You can, however, override it to provide a different “quit” message or to perform some actions.

      +

      Our building menu creates a choice at the end of our choice list if it’s a top-level menu (sub-menus +don’t have this feature). You can, however, override it to provide a different “quit” message or to +perform some actions.

      I encourage you to play with this code. As simple as it is, it offers some functionalities already.

      Customizing building menus

      -

      This somewhat long section explains how to customize building menus. There are different ways depending on what you would like to achieve. We’ll go from specific to more advanced here.

      +

      This somewhat long section explains how to customize building menus. There are different ways +depending on what you would like to achieve. We’ll go from specific to more advanced here.

      Generic choices

      -

      In the previous example, we’ve used add_choice. This is one of three methods you can use to add choices. The other two are to handle more generic actions:

      +

      In the previous example, we’ve used add_choice. This is one of three methods you can use to add +choices. The other two are to handle more generic actions:

        -
      • add_choice_edit: this is called to add a choice which points to the EvEditor. It is used to edit a description in most cases, although you could edit other things. We’ll see an example shortly. add_choice_edit uses most of the add_choice keyword arguments we’ll see, but usually we specify only two (sometimes three):

        +
      • add_choice_edit: this is called to add a choice which points to the EvEditor. It is used to +edit a description in most cases, although you could edit other things. We’ll see an example +shortly. add_choice_edit uses most of the add_choice keyword arguments we’ll see, but usually +we specify only two (sometimes three):

        • The choice title as usual.

        • The choice key (command key) as usual.

        • -
        • Optionally, the attribute of the object to edit, with the attr keyword argument. By default, attr contains db.desc. It means that this persistent data attribute will be edited by the EvEditor. You can change that to whatever you want though.

        • +
        • Optionally, the attribute of the object to edit, with the attr keyword argument. By +default, attr contains db.desc. It means that this persistent data attribute will be edited by +the EvEditor. You can change that to whatever you want though.

      • -
      • add_choice_quit: this allows to add a choice to quit the editor. Most advisable! If you don’t do it, the building menu will do it automatically, except if you really tell it not to. Again, you can specify the title and key of this menu. You can also call a function when this menu closes.

      • +
      • add_choice_quit: this allows to add a choice to quit the editor. Most advisable! If you don’t +do it, the building menu will do it automatically, except if you really tell it not to. Again, you +can specify the title and key of this menu. You can also call a function when this menu closes.

      -

      So here’s a more complete example (you can replace your RoomBuildingMenu class in commands/building.py to see it):

      +

      So here’s a more complete example (you can replace your RoomBuildingMenu class in +commands/building.py to see it):

       1
        2
        3
      @@ -395,7 +449,9 @@ As Account #1 you can create a demo/tutorial area with @batchcommand tutorial_wo
               self.add_choice_quit("quit this editor", "q")
       
      -

      So far, our building menu class is still thin… and yet we already have some interesting feature. See for yourself the following MUD client output (again, the commands are prefixed with > to distinguish them):

      +

      So far, our building menu class is still thin… and yet we already have some interesting feature. +See for yourself the following MUD client output (again, the commands are prefixed with > to +distinguish them):

      > @reload
       
       > @edit here
      @@ -440,25 +496,50 @@ A beautiful meadow(#2)
       This is a beautiful meadow.  But so beautiful I can't describe it.
       
      -

      So by using the d shortcut in our building menu, an EvEditor opens. You can use the EvEditor commands (like we did here, :DD to remove all, :wq to save and quit). When you quit the editor, the description is saved (here, in room.db.desc) and you go back to the building menu.

      -

      Notice that the choice to quit has changed too, which is due to our adding add_choice_quit. In most cases, you will probably not use this method, since the quit menu is added automatically.

      +

      So by using the d shortcut in our building menu, an EvEditor opens. You can use the EvEditor +commands (like we did here, :DD to remove all, :wq to save and quit). When you quit the editor, +the description is saved (here, in room.db.desc) and you go back to the building menu.

      +

      Notice that the choice to quit has changed too, which is due to our adding add_choice_quit. In +most cases, you will probably not use this method, since the quit menu is added automatically.

      add_choice options

      -

      add_choice and the two methods add_choice_edit and add_choice_quit take a lot of optional arguments to make customization easier. Some of these options might not apply to add_choice_edit or add_choice_quit however.

      +

      add_choice and the two methods add_choice_edit and add_choice_quit take a lot of optional +arguments to make customization easier. Some of these options might not apply to add_choice_edit +or add_choice_quit however.

      Below are the options of add_choice, specify them as arguments:

        -
      • The first positional, mandatory argument is the choice title, as we have seen. This will influence how the choice appears in the menu.

      • -
      • The second positional, mandatory argument is the command key to access to this menu. It is best to use keyword arguments for the other arguments.

      • -
      • The aliases keyword argument can contain a list of aliases that can be used to access to this menu. For instance: add_choice(..., aliases=['t'])

      • -
      • The attr keyword argument contains the attribute to edit when this choice is selected. It’s a string, it has to be the name, from the object (specified in the menu constructor) to reach this attribute. For instance, a attr of "key" will try to find obj.key to read and write the attribute. You can specify more complex attribute names, for instance, attr="db.desc" to set the desc persistent attribute, or attr="ndb.something" so use a non-persistent data attribute on the object.

      • -
      • The text keyword argument is used to change the text that will be displayed when the menu choice is selected. Menu choices provide a default text that you can change. Since this is a long text, it’s useful to use multi-line strings (see an example below).

      • -
      • The glance keyword argument is used to specify how to display the current information while in the menu, when the choice hasn’t been opened. If you examine the previous examples, you will see that the current (key or db.desc) was shown in the menu, next to the command key. This is useful for seeing at a glance the current value (hence the name). Again, menu choices will provide a default glance if you don’t specify one.

      • -
      • The on_enter keyword argument allows to add a callback to use when the menu choice is opened. This is more advanced, but sometimes useful.

      • -
      • The on_nomatch keyword argument is called when, once in the menu, the caller enters some text that doesn’t match any command (including the @ command). By default, this will edit the specified attr.

      • -
      • The on_leave keyword argument allows to specify a callback used when the caller leaves the menu choice. This can be useful for cleanup as well.

      • +
      • The first positional, mandatory argument is the choice title, as we have seen. This will +influence how the choice appears in the menu.

      • +
      • The second positional, mandatory argument is the command key to access to this menu. It is best +to use keyword arguments for the other arguments.

      • +
      • The aliases keyword argument can contain a list of aliases that can be used to access to this +menu. For instance: add_choice(..., aliases=['t'])

      • +
      • The attr keyword argument contains the attribute to edit when this choice is selected. It’s a +string, it has to be the name, from the object (specified in the menu constructor) to reach this +attribute. For instance, a attr of "key" will try to find obj.key to read and write the +attribute. You can specify more complex attribute names, for instance, attr="db.desc" to set the +desc persistent attribute, or attr="ndb.something" so use a non-persistent data attribute on the +object.

      • +
      • The text keyword argument is used to change the text that will be displayed when the menu choice +is selected. Menu choices provide a default text that you can change. Since this is a long text, +it’s useful to use multi-line strings (see an example below).

      • +
      • The glance keyword argument is used to specify how to display the current information while in +the menu, when the choice hasn’t been opened. If you examine the previous examples, you will see +that the current (key or db.desc) was shown in the menu, next to the command key. This is +useful for seeing at a glance the current value (hence the name). Again, menu choices will provide +a default glance if you don’t specify one.

      • +
      • The on_enter keyword argument allows to add a callback to use when the menu choice is opened. +This is more advanced, but sometimes useful.

      • +
      • The on_nomatch keyword argument is called when, once in the menu, the caller enters some text +that doesn’t match any command (including the @ command). By default, this will edit the +specified attr.

      • +
      • The on_leave keyword argument allows to specify a callback used when the caller leaves the menu +choice. This can be useful for cleanup as well.

      -

      These are a lot of possibilities, and most of the time you won’t need them all. Here is a short example using some of these arguments (again, replace the RoomBuildingMenu class in commands/building.py with the following code to see it working):

      +

      These are a lot of possibilities, and most of the time you won’t need them all. Here is a short +example using some of these arguments (again, replace the RoomBuildingMenu class in +commands/building.py with the following code to see it working):

       1
        2
        3
      @@ -532,17 +613,26 @@ This is a beautiful meadow.  But so beautiful I can't describe it.
       Closing the building menu.
       
      -

      The most surprising part is no doubt the text. We use the multi-line syntax (with """). Excessive spaces will be removed from the left for each line automatically. We specify some information between braces… sometimes using double braces. What might be a bit odd:

      +

      The most surprising part is no doubt the text. We use the multi-line syntax (with """). +Excessive spaces will be removed from the left for each line automatically. We specify some +information between braces… sometimes using double braces. What might be a bit odd:

      • {back} is a direct format argument we’ll use (see the .format specifiers).

      • -
      • {{obj...}} refers to the object being edited.  We use two braces, because .format` will remove them.

      • +
      • {{obj...}} refers to the object being edited.  We use two braces, because .format` will remove +them.

      In glance, we also use {obj.key} to indicate we want to show the room’s key.

      Everything can be a function

      -

      The keyword arguments of add_choice are often strings (type str). But each of these arguments can also be a function. This allows for a lot of customization, since we define the callbacks that will be executed to achieve such and such an operation.

      -

      To demonstrate, we will try to add a new feature. Our building menu for rooms isn’t that bad, but it would be great to be able to edit exits too. So we can add a new menu choice below description… but how to actually edit exits? Exits are not just an attribute to set: exits are objects (of type Exit by default) which stands between two rooms (object of type Room). So how can we show that?

      +

      The keyword arguments of add_choice are often strings (type str). But each of these arguments +can also be a function. This allows for a lot of customization, since we define the callbacks that +will be executed to achieve such and such an operation.

      +

      To demonstrate, we will try to add a new feature. Our building menu for rooms isn’t that bad, but +it would be great to be able to edit exits too. So we can add a new menu choice below +description… but how to actually edit exits? Exits are not just an attribute to set: exits are +objects (of type Exit by default) which stands between two rooms (object of type Room). So how +can we show that?

      First let’s add a couple of exits in limbo, so we have something to work with:

      @tunnel n
       @tunnel s
      @@ -560,8 +650,10 @@ This is a beautiful meadow.  But so beautiful I can't describe it.
       [<Exit: north>, <Exit: south>]
       
      -

      So what we need is to display this list in our building menu… and to allow to edit it would be great. Perhaps even add new exits?

      -

      First of all, let’s write a function to display the glance on existing exits. Here’s the code, it’s explained below:

      +

      So what we need is to display this list in our building menu… and to allow to edit it would be +great. Perhaps even add new exits?

      +

      First of all, let’s write a function to display the glance on existing exits. Here’s the code, +it’s explained below:

       1
        2
        3
      @@ -627,7 +719,10 @@ This is a beautiful meadow.  But so beautiful I can't describe it.
           return "\n  |gNo exit yet|n"
       
      -

      When the building menu opens, it displays each choice to the caller. A choice is displayed with its title (rendered a bit nicely to show the key as well) and the glance. In the case of the exits choice, the glance is a function, so the building menu calls this function giving it the object being edited (the room here). The function should return the text to see.

      +

      When the building menu opens, it displays each choice to the caller. A choice is displayed with its +title (rendered a bit nicely to show the key as well) and the glance. In the case of the exits +choice, the glance is a function, so the building menu calls this function giving it the object +being edited (the room here). The function should return the text to see.

      > @edit here
       Building menu: A beautiful meadow
       
      @@ -646,25 +741,41 @@ This is a beautiful meadow.  But so beautiful I can't describe it.
       

      How do I know the parameters of the function to give?

      -

      The function you give can accept a lot of different parameters. This allows for a flexible approach but might seem complicated at first. Basically, your function can accept any parameter, and the building menu will send only the parameter based on their names. If your function defines an argument named caller for instance (like def func(caller): ), then the building menu knows that the first argument should contain the caller of the building menu. Here are the arguments, you don’t have to specify them (if you do, they need to have the same name):

      +

      The function you give can accept a lot of different parameters. This allows for a flexible approach +but might seem complicated at first. Basically, your function can accept any parameter, and the +building menu will send only the parameter based on their names. If your function defines an +argument named caller for instance (like def func(caller): ), then the building menu knows that +the first argument should contain the caller of the building menu. Here are the arguments, you +don’t have to specify them (if you do, they need to have the same name):

        -
      • menu: if your function defines an argument named menu, it will contain the building menu itself.

      • -
      • choice: if your function defines an argument named choice, it will contain the Choice object representing this menu choice.

      • -
      • string: if your function defines an argument named string, it will contain the user input to reach this menu choice. This is not very useful, except on nomatch callbacks which we’ll see later.

      • -
      • obj: if your function defines an argument named obj, it will contain the building menu edited object.

      • -
      • caller: if your function defines an argument named caller, it will contain the caller of the building menu.

      • +
      • menu: if your function defines an argument named menu, it will contain the building menu +itself.

      • +
      • choice: if your function defines an argument named choice, it will contain the Choice object +representing this menu choice.

      • +
      • string: if your function defines an argument named string, it will contain the user input to +reach this menu choice. This is not very useful, except on nomatch callbacks which we’ll see +later.

      • +
      • obj: if your function defines an argument named obj, it will contain the building menu edited +object.

      • +
      • caller: if your function defines an argument named caller, it will contain the caller of the +building menu.

      • Anything else: any other argument will contain the object being edited by the building menu.

      So in our case:

      1
      def glance_exits(room):
       
      -

      The only argument we need is room. It’s not present in the list of possible arguments, so the editing object of the building menu (the room, here) is given.

      +

      The only argument we need is room. It’s not present in the list of possible arguments, so the +editing object of the building menu (the room, here) is given.

      Why is it useful to get the menu or choice object?

      -

      Most of the time, you will not need these arguments. In very rare cases, you will use them to get specific data (like the default attribute that was set). This tutorial will not elaborate on these possibilities. Just know that they exist.

      -

      We should also define a text callback, so that we can enter our menu to see the room exits. We’ll see how to edit them in the next section but this is a good opportunity to show a more complete callback. To see it in action, as usual, replace the class and functions in commands/building.py:

      +

      Most of the time, you will not need these arguments. In very rare cases, you will use them to get +specific data (like the default attribute that was set). This tutorial will not elaborate on these +possibilities. Just know that they exist.

      +

      We should also define a text callback, so that we can enter our menu to see the room exits. We’ll +see how to edit them in the next section but this is a good opportunity to show a more complete +callback. To see it in action, as usual, replace the class and functions in commands/building.py:

       1
        2
        3
      @@ -772,7 +883,9 @@ This is a beautiful meadow.  But so beautiful I can't describe it.
           return text
       
      -

      Look at the second callback in particular. It takes an additional argument, the caller (remember, the argument names are important, their order is not relevant). This is useful for displaying destination of exits accurately. Here is a demonstration of this menu:

      +

      Look at the second callback in particular. It takes an additional argument, the caller (remember, +the argument names are important, their order is not relevant). This is useful for displaying +destination of exits accurately. Here is a demonstration of this menu:

      > @edit here
       Building menu: A beautiful meadow
       
      @@ -813,15 +926,30 @@ This is a beautiful meadow.  But so beautiful I can't describe it.