diff --git a/docs/1.0-dev/.buildinfo b/docs/1.0-dev/.buildinfo
index 049458006d..1362d1ac51 100644
--- a/docs/1.0-dev/.buildinfo
+++ b/docs/1.0-dev/.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: 9adbe4f91e54ddd9ee1242ebb9714806
+config: 20e6a00accd2c45a252d90d346328ff4
tags: 645f666f9bcd5a90fca523b33c5a78b7
diff --git a/docs/1.0-dev/Components/Accounts.html b/docs/1.0-dev/Components/Accounts.html
index 68b3e1bbeb..9273107a7d 100644
--- a/docs/1.0-dev/Components/Accounts.html
+++ b/docs/1.0-dev/Components/Accounts.html
@@ -18,7 +18,7 @@
-
+
An Account represents a unique game account - one player playing the game. Whereas a player can potentially connect to the game from several Clients/Sessions, they will normally have only one Account.
The Account object has no in-game representation. In order to actually get on the game the Account must puppet an Object (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
Apart from storing login information and other account-specific data, the Account object is what is chatting on Evennia’s default Channels. It is also a good place to store Permissions to be consistent between different in-game characters. It can also hold player-level configuration options.
The Account object has its own default CmdSet, the AccountCmdSet. The commands in this set are available to the player no matter which character they are puppeting. Most notably the default game’s exit, who and chat-channel commands are in the Account cmdset.
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 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 permissions this way - for hierarchical permissions like Builder, Admin etc, the lower of the permissions on the Character/Account will always be used).
You will usually not want more than one Account typeclass for all new accounts.
An Evennia Account is, per definition, a Python class that includes evennia.DefaultAccount among its parents. In mygame/typeclasses/accounts.py there is an empty class ready for you to modify. Evennia defaults to using this (it inherits directly from DefaultAccount).
Here’s an example of modifying the default Account class in code:
Attributes allow you to to store arbitrary data on objects and make sure the data survives a server reboot. An Attribute can store pretty much any
-Python data structure and data type, like numbers, strings, lists, dicts etc. You can also
-store (references to) database objects like characters and rooms.
-
-
What can be stored in an Attribute is a must-read to avoid being surprised, also for experienced developers. Attributes can store almost everything
-but you need to know the quirks.
-
NAttributes are the in-memory, non-persistent
-siblings of Attributes.
Attributes are usually handled in code. All Typeclassed entities
-(Accounts, Objects, Scripts and
-Channels) can (and usually do) have Attributes associated with them. There
+
Attributes allow you to to store arbitrary data on objects and make sure the data survives a server reboot. An Attribute can store pretty much any Python data structure and data type, like numbers, strings, lists, dicts etc. You can also store (references to) database objects like characters and rooms.
Attributes are usually handled in code. All Typeclassed entities (Accounts, Objects, Scripts and Channels) can (and usually do) have Attributes associated with them. There
are three ways to manage Attributes, all of which can be mixed.
The simplest way to get/set Attributes is to use the .db shortcut. This allows for setting and getting Attributes that lack a category (having category None)
Here are the methods of the AttributeHandler. See the AttributeHandler API for more details.
has(...) - this checks if the object has an Attribute with this key. This is equivalent
to doing obj.db.attrname except you can also check for a specific `category.
@@ -330,16 +313,45 @@ The drawback is that without a database precense you can’t find the Attribute
char.attributes.get("sleepy")# now returns Truechar.sleepy# now returns True, involves db access
-
-
You can e.g. delchar.strength to set the value back to the default (the value defined
-in the AttributeProperty).
+
You can e.g. delchar.strength to set the value back to the default (the value defined in the AttributeProperty).
See the AttributeProperty API for more details on how to create it with special options, like giving access-restrictions.
An Attribute object is stored in the database. It has the following properties:
+
+
key - the name of the Attribute. When doing e.g. obj.db.attrname=value, this property is set
+to 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
+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
+
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.
+
+
There are also two special properties:
+
+
attrtype - this is used internally by Evennia to separate Nicks, from Attributes (Nicks
+use Attributes behind the scenes).
+
model - this is a natural-key describing the model this Attribute is attached to. This is on
+the form appname.modelclass, like objects.objectdb. It is used by the Attribute and
+NickHandler to quickly sort matches in the database. Neither this nor attrtype should normally
+need to be modified.
+
+
Non-database attributes are not stored in the database and have no equivalence
+to category nor strvalue, attrtype or model.
Attributes are mainly used by code. But one can also allow the builder to use Attributes to
‘turn knobs’ in-game. For example a builder could want to manually tweak the “level” Attribute of an
enemy NPC to lower its difficuly.
@@ -375,7 +387,7 @@ set mypobj/mystring = [1, 2, foo] # foo is invalid Python (no quotes)
For the last line you’ll get a warning and the value instead will be saved as a string "[1,2,foo]".
While the set command is limited to builders, individual Attributes are usually not
locked down. You may want to lock certain sensitive Attributes, in particular for games
where you allow player building. You can add such limitations by adding a lock string
@@ -418,6 +430,7 @@ To check the lockst
The same keywords are available to use with obj.attributes.set() and obj.attributes.remove(),
those will check for the attredit lock type.
The database doesn’t know anything about Python objects, so Evennia must serialize Attribute
@@ -613,38 +626,6 @@ instead of _SaverLi
explicitly save it back to the Attribute for it to save.
An Attribute object is stored in the database. It has the following properties:
-
-
key - the name of the Attribute. When doing e.g. obj.db.attrname=value, this property is set
-to 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
-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
-
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.
-
-
There are also two special properties:
-
-
attrtype - this is used internally by Evennia to separate Nicks, from Attributes (Nicks
-use Attributes behind the scenes).
-
model - this is a natural-key describing the model this Attribute is attached to. This is on
-the form appname.modelclass, like objects.objectdb. It is used by the Attribute and
-NickHandler to quickly sort matches in the database. Neither this nor attrtype should normally
-need to be modified.
-
-
Non-database attributes are not stored in the database and have no equivalence
-to category nor strvalue, attrtype or model.
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).
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).
The batch-code processor is a superuser-only function, invoked by
-
> @batchcode path.to.batchcodefile
+
> 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
-
> @batchcode batch_code
+
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.
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.
-
Here are the rules of syntax of the batch-code *.py file.
+
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.
-
#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!
-
#INSERTpath.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.
+
#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!
+
#INSERTpath.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.
Below is a version of the example file found in evennia/contrib/tutorial_examples/.
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 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.
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.
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).
fromevennia.utilsimportcreate,searchfromevennia.contrib.tutorial_examplesimportred_buttonfromtypeclasses.objectsimportObject
@@ -258,21 +216,14 @@ file. Observe that the block has not actually been executed at this poi
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
+
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. nn12 will jump 12 steps forward
-(without processing any blocks in between). All normal commands of Evennia should work too while
-working in interactive mode.
+
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. nn12 will jump 12 steps forward (without processing any blocks in between). All normal commands of Evennia should work too while working in interactive mode.
Or rather the lack of it. There is a reason only superusers are allowed to run the batch-code
@@ -286,24 +237,11 @@ really do anything outside what the game commands allow them to.
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:
#HEADER ifcaller.ndb.all_roomsisNone:caller.ndb.all_rooms={}
@@ -320,23 +258,16 @@ dictionary of room references in an <
-
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
+
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.
@@ -357,13 +288,14 @@ executed. When the code runs it has no knowledge of what file those strings wher
modules |
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).
The batch-command processor is a superuser-only function, invoked by
-
> @batchcommand path.to.batchcmdfile
+
> batchcommand path.to.batchcmdfile
Where path.to.batchcmdfile is the path to a batch-command file with the “.ev” file ending.
@@ -124,7 +122,7 @@ This path is given like a python path relative to a folder you define to hold yo
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
+
> batchcommand batch_cmds
A batch-command file contains a list of Evennia in-game commands separated by comments. The
@@ -132,30 +130,18 @@ processor will run the batch file from beginning to end. Note that it will n
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 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.
#
@@ -201,23 +187,17 @@ batch-files together, use the
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 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.
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!
+
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. nn12 will jump 12 steps forward
-(without processing any command in between). All normal commands of Evennia should work too while
-working in interactive mode.
+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. nn12 will jump 12 steps forward (without processing any command in between). All normal commands of Evennia should work too while working in interactive mode.
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 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:
+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.
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.
@@ -301,13 +249,14 @@ mode instead, see its readme for install instructions.
modules |
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
+
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:
+
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.
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.
+
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
+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.
@@ -143,15 +144,12 @@ it to every other subscriber.
Changed in version 1.0: Channel system changed to use a central ‘channel’ command and nicks instead of
auto-generated channel-commands and -cmdset. ChannelHandler was removed.
In the default command set, channels are all handled via the mighty
-channel
-command, channel (or
-chan). By default, this command will assume all entities dealing with
-channels are Accounts.
In the default command set, channels are all handled via the mighty channel command, channel (or chan). By default, this command will assume all entities dealing with channels are Accounts.
+
Viewing channels
channel - shows your subscriptions
channel/all - shows all subs available to you
channel/who - shows who subscribes to this channel
@@ -169,8 +167,8 @@ channel/unmute channelname
Banning adds the user to the channels blacklist. This means they will not be
able to rejoin if you boot them. You will need to run channel/boot to
actually kick them out.
-
See the Channel command api
-docs (and in-game help) for more details.
+
See the Channel command api docs (and in-game help) for more details.
Admin-level users can also modify channel’s locks:
By default everyone can use the channel command (evennia.commands.default.comms.CmdChannel)
-to create channels and will then control the channels they created (to boot/ban
-people etc). If you as a developer does not want regular players to do this
-(perhaps you want only staff to be able to spawn new channels), you can
-override the channel command and change its locks property.
+
By default everyone can use the channel command (evennia.commands.default.comms.CmdChannel) to create channels and will then control the channels they created (to boot/ban people etc). If you as a developer does not want regular players to do this (perhaps you want only staff to be able to spawn new channels), you can override the channel command and change its locks property.
The default help command has the following locks property:
For most common changes, the default channel, the recipient hooks and possibly
+overriding the channel command will get you very far. But you can also tweak
+channels themselves.
The default channel command (evennia.commands.default.comms.CmdChannel)
-sits in the Accountcommand set. It is set up such that it will
-always operate on Accounts, even if you were to add it to the
-CharacterCmdSet.
-
It’s a one-line change to make this command accept non-account callers. But for
-convenience we provide a version for Characters/Objects. Just import
-evennia.commands.default.comms.CmdObjectChannel
-and inherit from that instead.
The default channel command (evennia.commands.default.comms.CmdChannel) sits in the Accountcommand set. It is set up such that it will always operate on Accounts, even if you were to add it to the CharacterCmdSet.
+
It’s a one-line change to make this command accept non-account callers. But for convenience we provide a version for Characters/Objects. Just import evennia.commands.default.comms.CmdObjectChannel and inherit from that instead.
When distributing a message, the channel will call a series of hooks on itself
and (more importantly) on each recipient. So you can customize things a lot by
just modifying hooks on your normal Object/Account typeclasses.
@@ -354,19 +346,10 @@ recipient is skipped.
So make sure you modify the set actually used by your subcribers (or both).
Default channels all use Account subscribers.
-
-
For most common changes, the default channel, the recipient hooks and possibly
-overriding the channel command will get you very far. But you can also tweak
-channels themselves.
-
Channels are Typeclassed entities. This means they are
-persistent in the database, can have attributes and Tags
-and can be easily extended.
-
To change which channel typeclass Evennia uses for default commands, change
-settings.BASE_CHANNEL_TYPECLASS. The base command class is
-evennia.comms.comms.DefaultChannel.
-There is an empty child class in mygame/typeclasses/channels.py, same
-as for other typelass-bases.
Channels are Typeclassed entities. This means they are persistent in the database, can have attributes and Tags and can be easily extended.
+
To change which channel typeclass Evennia uses for default commands, change settings.BASE_CHANNEL_TYPECLASS. The base command class is evennia.comms.comms.DefaultChannel. There is an empty child class in mygame/typeclasses/channels.py, same as for other typelass-bases.
In code you create a new channel with evennia.create_channel or
Channel.create:
fromevenniaimportcreate_channel,search_object
@@ -399,28 +382,24 @@ as for other typelass-bases.
The Channel’s .connect method will accept both Account and Object subscribers
and will handle them transparently.
-
The channel has many more hooks, both hooks shared with all typeclasses as well
-as special ones related to muting/banning etc. See the channel class for
+
The channel has many more hooks, both hooks shared with all typeclasses as well as special ones related to muting/banning etc. See the channel class for
details.
Changed in version 0.7: Channels changed from using Msg to TmpMsg and optional log files.
Changed in version 1.0: Channels stopped supporting Msg and TmpMsg, using only log files.
-
The channel messages are not stored in the database. A channel is instead
-always logged to a regular text log-file
-mygame/server/logs/channel_<channelname>.log. This is where channels/historychannelname
-gets its data from. A channel’s log will rotate when it grows too big, which
-thus also automatically limits the max amount of history a user can view with
+
The channel messages are not stored in the database. A channel is instead always logged to a regular text log-file mygame/server/logs/channel_<channelname>.log. This is where channels/historychannelname gets its data from. A channel’s log will rotate when it grows too big, which thus also automatically limits the max amount of history a user can view with
/history.
The log file name is set on the channel class as the log_file property. This
is a string that takes the formatting token {channelname} to be replaced with
the (lower-case) name of the channel. By default the log is written to in the
channel’s at_post_channel_msg method.
Channels have all the standard properties of a Typeclassed entity (key,
diff --git a/docs/1.0-dev/Components/Coding-Utils.html b/docs/1.0-dev/Components/Coding-Utils.html
index 39d97b9017..0ecc16c844 100644
--- a/docs/1.0-dev/Components/Coding-Utils.html
+++ b/docs/1.0-dev/Components/Coding-Utils.html
@@ -72,7 +72,7 @@
Evennia comes with many utilities to help with common coding tasks. Most are accessible directly
from the flat API, otherwise you can find them in the evennia/utils/ folder.
+
+
This is just a small selection of the tools in evennia/utils. It’s worth to browse the directory and in particular the content of evennia/utils/utils.py directly to find more useful stuff.
A common thing to do is to search for objects. There it’s easiest to use the search method defined
@@ -140,11 +137,8 @@ on all objects. This will search for objects in the same location and inside the
obj=self.search(objname)
-
The most common time one needs to do this is inside a command body. obj=self.caller.search(objname) will search inside the caller’s (typically, the character that typed
-the command) .contents (their “inventory”) and .location (their “room”).
-
Give the keyword global_search=True to extend search to encompass entire database. Aliases will
-also be matched by this search. You will find multiple examples of this functionality in the default
-command set.
+
The most common time one needs to do this is inside a command body. obj=self.caller.search(objname) will search inside the caller’s (typically, the character that typed the command) .contents (their “inventory”) and .location (their “room”).
+
Give the keyword global_search=True to extend search to encompass entire database. Aliases will also be matched by this search. You will find multiple examples of this functionality in the default command set.
If you need to search for objects in a code module you can use the functions in
evennia.utils.search. You can access these as shortcuts evennia.search_*.
Apart from the in-game build commands (@create etc), you can also build all of Evennia’s game
-entities directly in code (for example when defining new create commands).
+
Apart from the in-game build commands (@create etc), you can also build all of Evennia’s game entities directly in code (for example when defining new create commands).
Normally you can use Python print statements to see output to the terminal/log. The print
-statement should only be used for debugging though. For producion output, use the logger which
-will create proper logs either to terminal or to file.
+statement should only be used for debugging though. For producion output, use the logger which will create proper logs either to terminal or to file.
fromevenniaimportlogger#logger.log_err("This is an Error!")
@@ -196,8 +186,7 @@ will create proper logs either to terminal or to file.
logger.log_dep("This feature is deprecated")
-
There is a special log-message type, log_trace() that is intended to be called from inside a
-traceback - this can be very useful for relaying the traceback message back to log without having it
+
There is a special log-message type, log_trace() that is intended to be called from inside a traceback - this can be very useful for relaying the traceback message back to log without having it
kill the server.
try:# [some code that may fail...]
@@ -205,24 +194,18 @@ kill the server.
logger.log_trace("This text will show beneath the traceback itself.")
-
The log_file logger, finally, is a very useful logger for outputting arbitrary log messages. This
-is a heavily optimized asynchronous log mechanism using
-threads to avoid overhead. You should be
-able to use it for very heavy custom logging without fearing disk-write delays.
+
The log_file logger, finally, is a very useful logger for outputting arbitrary log messages. This is a heavily optimized asynchronous log mechanism using threads to avoid overhead. You should be able to use it for very heavy custom logging without fearing disk-write delays.
logger.log_file(message,filename="mylog.log")
-
If not an absolute path is given, the log file will appear in the mygame/server/logs/ directory.
-If the file already exists, it will be appended to. Timestamps on the same format as the normal
-Evennia logs will be automatically added to each entry. If a filename is not specified, output will
-be written to a file game/logs/game.log.
+
If not an absolute path is given, the log file will appear in the mygame/server/logs/ directory. If the file already exists, it will be appended to. Timestamps on the same format as the normal Evennia logs will be automatically added to each entry. If a filename is not specified, output will be written to a file game/logs/game.log.
+
See also the Debugging documentation for help with finding elusive bugs.
Evennia tracks the current server time. You can access this time via the evennia.gametime
-shortcut:
+
Evennia tracks the current server time. You can access this time via the evennia.gametime shortcut:
fromevenniaimportgametime# all the functions below return times in seconds).
@@ -247,12 +230,8 @@ shortcut:
-
The setting TIME_FACTOR determines how fast/slow in-game time runs compared to the real world. The
-setting TIME_GAME_EPOCH sets the starting game epoch (in seconds). The functions from the
-gametime module all return their times in seconds. You can convert this to whatever units of time
-you desire for your game. You can use the @time command to view the server time info.
-
You can also schedule things to happen at specific in-game times using the
-gametime.schedule function:
+
The setting TIME_FACTOR determines how fast/slow in-game time runs compared to the real world. The setting TIME_GAME_EPOCH sets the starting game epoch (in seconds). The functions from the gametime module all return their times in seconds. You can convert this to whatever units of time you desire for your game. You can use the @time command to view the server time info.
+You can also schedule things to happen at specific in-game times using the gametime.schedule function:
importevenniadefchurch_clock:
@@ -265,9 +244,7 @@ you desire for your game. You can use the
This function takes a number of seconds as input (e.g. from the gametime module above) and
-converts it to a nice text output in days, hours etc. It’s useful when you want to show how old
-something is. It converts to four different styles of output using the style keyword:
+
This function takes a number of seconds as input (e.g. from the gametime module above) and converts it to a nice text output in days, hours etc. It’s useful when you want to show how old something is. It converts to four different styles of output using the style keyword:
style 0 - 5d:45m:12s (standard colon output)
style 1 - 5d (shows only the longest time unit)
@@ -277,50 +254,30 @@ something is. It converts to four different styles of output using the style
fromevenniaimportutilsdef_callback(obj,text):obj.msg(text)# wait 10 seconds before sending "Echo!" to obj (which we assume is defined)
-deferred=utils.delay(10,_callback,obj,"Echo!",persistent=False)
+utils.delay(10,_callback,obj,"Echo!",persistent=False)# code here will run immediately, not waiting for the delay to fire!
-
This creates an asynchronous delayed call. It will fire the given callback function after the given
-number of seconds. This is a very light wrapper over a Twisted
-Deferred. Normally this is run
-non-persistently, which means that if the server is @reloaded before the delay is over, the
-callback will never run (the server forgets it). If setting persistent to True, the delay will be
-stored in the database and survive a @reload - but for this to work it is susceptible to the same
-limitations incurred when saving to an Attribute.
-
The deferred return object can usually be ignored, but calling its .cancel() method will abort
-the delay prematurely.
-
utils.delay is the lightest form of delayed call in Evennia. For other way to create time-bound
-tasks, see the TickerHandler and Scripts.
-
-
Note that many delayed effects can be achieved without any need for an active timer. For example
-if you have a trait that should recover a point every 5 seconds you might just need its value when
-it’s needed, but checking the current time and calculating on the fly what value it should have.
-
+
See The Asynchronous process for more information.
This useful function takes two arguments - an object to check and a parent. It returns True if
-object inherits from parent at any distance (as opposed to Python’s in-built is_instance() that
+
This useful function takes two arguments - an object to check and a parent. It returns True if object inherits from parent at any distance (as opposed to Python’s in-built is_instance() that
will only catch immediate dependence). This function also accepts as input any combination of
classes, instances or python-paths-to-classes.
-
Note that Python code should usually work with duck
-typing. But in Evennia’s case it can sometimes be useful
-to check if an object inherits from a given Typeclass as a way of identification. Say
-for example that we have a typeclass Animal. This has a subclass Felines which in turn has a
-subclass HouseCat. Maybe there are a bunch of other animal types too, like horses and dogs. Using
-inherits_from will allow you to check for all animals in one go:
+
Note that Python code should usually work with duck typing. But in Evennia’s case it can sometimes be useful to check if an object inherits from a given Typeclass as a way of identification. Say for example that we have a typeclass Animal. This has a subclass Felines which in turn has a subclass HouseCat. Maybe there are a bunch of other animal types too, like horses and dogs. Using inherits_from will allow you to check for all animals in one go:
fromevenniaimportutilsif(utils.inherits_from(obj,"typeclasses.objects.animals.Animal"):obj.msg("The bouncer stops you in the door. He says: 'No talking animals allowed.'")
@@ -335,8 +292,7 @@ complete selection of text utilities found in
This solves what may at first glance appear to be a trivial problem with text - removing
-indentations. It is used to shift entire paragraphs to the left, without disturbing any further
-formatting they may have. A common case for this is when using Python triple-quoted strings in code
-
-
they will retain whichever indentation they have in the code, and to make easily-readable source
-code one usually don’t want to shift the string to the left edge.
-
+
This solves what may at first glance appear to be a trivial problem with text - removing indentations. It is used to shift entire paragraphs to the left, without disturbing any further formatting they may have. A common case for this is when using Python triple-quoted strings in code - they will retain whichever indentation they have in the code, and to make easily-readable source code one usually don’t want to shift the string to the left edge.
#python code is entered at a given indentationintxt=""" This is an example text that will end
@@ -376,40 +326,8 @@ help entries).
Evennia supplies two utility functions for converting text to the correct
-encodings. to_str() and to_bytes(). Unless you are adding a custom protocol and
-need to send byte-data over the wire, to_str is the only one you’ll need.
-
The difference from Python’s in-built str() and bytes() operators are that
-the Evennia ones makes use of the ENCODINGS setting and will try very hard to
-never raise a traceback but instead echo errors through logging. See
-here for more info.
The EvTable class (evennia/utils/evtable.py) can be used
-to create correctly formatted text tables. There is also
-EvForm (evennia/utils/evform.py). This reads a fixed-format
-text template from a file in order to create any level of sophisticated ascii layout. Both evtable
-and evform have lots of options and inputs so see the header of each module for help.
-
The third-party PrettyTable module is also included in
-Evennia. PrettyTable is considered deprecated in favor of EvTable since PrettyTable cannot handle
-ANSI colour. PrettyTable can be found in evennia/utils/prettytable/. See its homepage above for
-instructions.
Evennia supplies two utility functions for converting text to the correct encodings. to_str() and to_bytes(). Unless you are adding a custom protocol and need to send byte-data over the wire, to_str is the only one you’ll need.
+
The difference from Python’s in-built str() and bytes() operators are that the Evennia ones makes use of the ENCODINGS setting and will try very hard to never raise a traceback but instead echo errors through logging. See here for more info.
@@ -137,8 +137,7 @@ be familiar with how the command system works. The two pages were split for easy
A Command is a python class containing all the functioning code for what a command does - for example, a get command would contain code for picking up objects.
A Command Set (often referred to as a CmdSet or cmdset) is like a container for one or more Commands. A given Command can go into any number of different command sets. Only by putting the command set on a character object you will make all the commands therein available to use by that character. You can also store command sets on normal objects if you want users to be able to use the object in various ways. Consider a “Tree” object with a cmdset defining the commands climb and chop down. Or a “Clock” with a cmdset containing the single command check time.
-
This page goes into full detail about how to use Commands. To fully use them you must also read the page detailing Command Sets. There is also a step-by-step Adding Command Tutorial that will get you started quickly without the
-extra explanations.
+
This page goes into full detail about how to use Commands. To fully use them you must also read the page detailing Command Sets. There is also a step-by-step Adding Command Tutorial that will get you started quickly without the extra explanations.
All commands are implemented as normal Python classes inheriting from the base class Command
@@ -230,7 +229,7 @@ NPC).
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 lookatsword 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 lookatsword 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).
+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.
@@ -254,7 +253,7 @@ truthfully report this value - that case the 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.
+
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.
@@ -599,7 +598,7 @@ thus do so asynchronously, using callbacks.
next |
These are the ‘building blocks’ out of which Evennia is built. This documentation is complementary to, and often goes deeper than, the doc-strings of each component in the API.
When you first connect to your game you are greeted by Evennia’s default connection screen.
-
==============================================================
- Welcome to Evennia, version Beta-ra4d24e8a3cab+!
-
- If you have an existing account, connect to it by typing:
- connect <username> <password>
- If you need to create an account, type (without the <>'s):
- create <username> <password>
-
- If you have spaces in your username, enclose it in quotes.
- Enter help for more info. look will re-show this screen.
-==============================================================
-
-
-
Effective, but not very exciting. You will most likely want to change this to be more unique for
-your game. This is simple:
Evennia will look into this module and locate all globally defined strings in it. These strings
-are used as the text in your connection screen and are shown to the user at startup. If more than
-one such string/screen is defined in the module, a random screen will be picked from among those
-available.
You can also customize the Commands available to use while the connection screen is
-shown (connect, create etc). These commands are a bit special since when the screen is running
-the account is not yet logged in. A command is made available at the login screen by adding them to
-UnloggedinCmdSet in mygame/commands/default_cmdset.py. See Commands and the
-tutorial section on how to add new commands to a default command set.
If you set the persistent keyword to True when creating the editor, it will remain open even
when reloading the game. In order to be persistent, an editor needs to have its callback functions
(loadfunc, savefunc and quitfunc) as top-level functions defined in the module. Since these
@@ -214,7 +215,7 @@ functions will be stored, Python will need to find them.
The EvEditor is also used to edit some Python code in Evennia. The @py command supports an
-/edit switch that will open the EvEditor in code mode. This mode isn’t significantly different
-from the standard one, except it handles automatic indentation of blocks and a few options to
-control this behavior.
The EvEditor is also used to edit some Python code in Evennia. The py command supports an /edit switch that will open the EvEditor in code mode. This mode isn’t significantly different from the standard one, except it handles automatic indentation of blocks and a few options to control this behavior.
:< to remove a level of indentation for the future lines.
:+ to add a level of indentation for the future lines.
:= to disable automatic indentation altogether.
-
Automatic indentation is there to make code editing more simple. Python needs correct indentation,
-not as an aesthetic addition, but as a requirement to determine beginning and ending of blocks. The
-EvEditor will try to guess the next level of indentation. If you type a block “if”, for instance,
-the EvEditor will propose you an additional level of indentation at the next line. This feature
-cannot be perfect, however, and sometimes, you will have to use the above options to handle
-indentation.
+
Automatic indentation is there to make code editing more simple. Python needs correct indentation, not as an aesthetic addition, but as a requirement to determine beginning and ending of blocks. The EvEditor will try to guess the next level of indentation. If you type a block “if”, for instance, the EvEditor will propose you an additional level of indentation at the next line. This feature cannot be perfect, however, and sometimes, you will have to use the above options to handle indentation.
:= can be used to turn automatic indentation off completely. This can be very useful when trying
to paste several lines of code that are already correctly indented, for instance.
-
To see the EvEditor in code mode, you can use the @py/edit command. Type in your code (on one or
-several lines). You can then use the :w option (save without quitting) and the code you have
+
To see the EvEditor in code mode, you can use the @py/edit command. Type in your code (on one or several lines). You can then use the :w option (save without quitting) and the code you have
typed will be executed. The :! will do the same thing. Executing code while not closing the
editor can be useful if you want to test the code you have typed but add new lines after your test.
EvMenu is used for generate branching multi-choice menus. Each menu ‘node’ can
+
Is your answer yes or no?
+_________________________________________
+[Y]es! - Answer yes.
+[N]o! - Answer no.
+[A]bort - Answer neither, and abort.
+
+> Y
+You chose yes!
+
+Thanks for your answer. Goodbye!
+
+
+
EvMenu is used for generate branching multi-choice menus. Each menu ‘node’ can
accepts specific options as input or free-form input. Depending what the player
chooses, they are forwarded to different nodes in the menu.
The EvMenu utility class is located in evennia/utils/evmenu.py.
It allows for easily adding interactive menus to the game; for example to implement Character
creation, building commands or similar. Below is an example of offering NPC conversation choices:
This section gives some examples of how menus work in-game. A menu is a state
-(it’s actually a custom cmdset) where menu-specific commands are made available
-to you. An EvMenu is usually started from inside a command, but could also
-just be put in a file and run with py.
-
This is how the example menu will look in-game:
-
Is your answer yes or no?
-_________________________________________
-[Y]es! - Answer yes.
-[N]o! - Answer no.
-[A]bort - Answer neither, and abort.
-
-
-
If you pick (for example) Y(es), you will see
-
You chose yes!
-
-Thanks for your answer. Goodbye!
-
-
-
After which the menu will end (in this example at least - it could also continue
-on to other questions and choices or even repeat the same node over and over!)
-
Here’s the full EvMenu code for this example:
+
This is how the example menu at the top of this page will look in code:
fromevennia.utilsimportevmenudef_handle_answer(caller,raw_input,**kwargs):
@@ -317,8 +300,6 @@ uses the template-string and a mapping of callables (we must add
means depends on your game) to decide if your approach succeeded. It may then
choose to point you to nodes that continue the conversation or maybe dump you
into combat!
-
-
Initializing the menu is done using a call to the evennia.utils.evmenu.EvMenu class. This is the
@@ -354,39 +335,14 @@ most common way to do so - from inside a
-
menu_data (str, module or dict): is a module or python path to a module where the global-level
-functions will each be considered to be a menu node. Their names in the module will be the names
-by which they are referred to in the module. Importantly, function names starting with an
-underscore
-_ will be ignored by the loader. Alternatively, this can be a direct mapping
+
caller (Object or Account): is a reference to the object using the menu. This object will get a new CmdSet assigned to it, for handling the menu.
+
menu_data (str, module or dict): is a module or python path to a module where the global-level functions will each be considered to be a menu node. Their names in the module will be the names by which they are referred to in the module. Importantly, function names starting with an underscore _ will be ignored by the loader. Alternatively, this can be a direct mapping
{"nodename":function,...}.
-
startnode (str): is the name of the menu-node to start the menu at. Changing this means that
-you can jump into a menu tree at different positions depending on circumstance and thus possibly
-re-use menu entries.
-
cmdset_mergetype (str): This is usually one of “Replace” or “Union” (see [CmdSets](Command-
-Sets).
-The first means that the menu is exclusive - the user has no access to any other commands while
-in the menu. The Union mergetype means the menu co-exists with previous commands (and may
-overload
-them, so be careful as to what to name your menu entries in this case).
-
cmdset_priority (int): The priority with which to merge in the menu cmdset. This allows for
-advanced usage.
-
auto_quit, auto_look, auto_help (bool): If either of these are True, the menu
-automatically makes a quit, look or help command available to the user. The main reason why
-you’d want to turn this off is if you want to use the aliases “q”, “l” or “h” for something in
-your
-menu. Nevertheless, at least quit is highly recommend - if False, the menu must itself
-supply
-an “exit node” (a node without any options), or the user will be stuck in the menu until the
-server
-reloads (or eternally if the menu is persistent)!
-
cmd_on_exit (str): This command string will be executed right after the menu has closed down.
-From experience, it’s useful to trigger a “look” command to make sure the user is aware of the
-change of state; but any command can be used. If set to None, no command will be triggered
-after
-exiting the menu.
+
startnode (str): is the name of the menu-node to start the menu at. Changing this means that you can jump into a menu tree at different positions depending on circumstance and thus possibly re-use menu entries.
+
cmdset_mergetype (str): This is usually one of “Replace” or “Union” (see [CmdSets](Command- Sets). The first means that the menu is exclusive - the user has no access to any other commands while in the menu. The Union mergetype means the menu co-exists with previous commands (and may overload them, so be careful as to what to name your menu entries in this case).
+
cmdset_priority (int): The priority with which to merge in the menu cmdset. This allows for advanced usage.
+
auto_quit, auto_look, auto_help (bool): If either of these are True, the menu automatically makes a quit, look or help command available to the user. The main reason why you’d want to turn this off is if you want to use the aliases “q”, “l” or “h” for something in your menu. Nevertheless, at least quit is highly recommend - if False, the menu must itself supply an “exit node” (a node without any options), or the user will be stuck in the menu until the server reloads (or eternally if the menu is persistent)!
+
cmd_on_exit (str): This command string will be executed right after the menu has closed down. From experience, it’s useful to trigger a “look” command to make sure the user is aware of the change of state; but any command can be used. If set to None, no command will be triggered after exiting the menu.
persistent (bool) - if True, the menu will survive a reload (so the user will not be kicked
out by the reload - make sure they can exit on their own!)
startnode_input (str or (str, dict) tuple): Pass an input text or a input text + kwargs to the
@@ -397,9 +353,7 @@ start a menu differently depending on the Command’s arguments in which it was
debug (bool): If set, the menudebug command will be made available in the menu. Use it to
list the current state of the menu and use menudebug<variable> to inspect a specific state
variable from the list.
-
All other keyword arguments will be available as initial data for the nodes. They will be
-available in all nodes as properties on caller.ndb._evmenu (see below). These will also
-survive a @reload if the menu is persistent.
+
All other keyword arguments will be available as initial data for the nodes. They will be available in all nodes as properties on caller.ndb._evmenu (see below). These will also survive a reload if the menu is persistent.
You don’t need to store the EvMenu instance anywhere - the very act of initializing it will store it
as caller.ndb._evmenu on the caller. This object will be deleted automatically when the menu
@@ -601,9 +555,8 @@ goto-callable. This functionality comes from a time before goto could be a calla
When the menu starts, the EvMenu instance is stored on the caller as caller.ndb._evmenu. Through
this object you can in principle reach the menu’s internal state if you know what you are doing.
This is also a good place to store temporary, more global variables that may be cumbersome to keep
@@ -614,7 +567,7 @@ that this will remain after the menu closes though, so you need to handle any ne
yourself.
The EvMenu display of nodes, options etc are controlled by a series of formatting methods on the
EvMenu class. To customize these, simply create a new child class of EvMenu and override as
needed. Here is an example:
@@ -678,6 +631,7 @@ needed. Here is an example:
See evennia/utils/evmenu.py for the details of their default implementations.
This describes two ways for asking for simple questions from the user. Using Python’s input
+will not work in Evennia. input will block the entire server for everyone until that one
+player has entered their text, which is not what you want.
In the func method of your Commands (only) you can use Python’s built-in yield command to
+request input in a similar way to input. It looks like this:
+
result=yield("Please enter your answer:")
+
+
+
This will send “Please enter your answer” to the Command’s self.caller and then pause at that
+point. All other players at the server will be unaffected. Once caller enteres a reply, the code
+execution will continue and you can do stuff with the result. Here is an example:
+
fromevenniaimportCommand
+classCmdTestInput(Command):
+ key="test"
+ deffunc(self):
+ result=yield("Please enter something:")
+ self.caller.msg(f"You entered {result}.")
+ result2=yield("Now enter something else:")
+ self.caller.msg(f"You now entered {result2}.")
+
+
+
Using yield is simple and intuitive, but it will only access input from self.caller and you
+cannot abort or time out the pause until the player has responded. Under the hood, it is actually
+just a wrapper calling get_input described in the following section.
+
+
Important Note: In Python you cannot mix yield and return<value> in the same method. It has
+to do with yield turning the method into a
+generator. A return without an argument works, you
+can just not do return<value>. This is usually not something you need to do in func() anyway,
+but worth keeping in mind.
The evmenu module offers a helper function named get_input. This is wrapped by the yield
+statement which is often easier and more intuitive to use. But get_input offers more flexibility
+and power if you need it. While in the same module as EvMenu, get_input is technically unrelated
+to it. The get_input allows you to ask and receive simple one-line input from the user without
+launching the full power of a menu to do so. To use, call get_input like this:
+
get_input(caller,prompt,callback)
+
+
+
Here caller is the entity that should receive the prompt for input given as prompt. The
+callback is a callable function(caller,prompt,user_input) that you define to handle the answer
+from the user. When run, the caller will see prompt appear on their screens and any text they
+enter will be sent into the callback for whatever processing you want.
+
Below is a fully explained callback and example call:
+
fromevenniaimportCommand
+fromevennia.utils.evmenuimportget_input
+
+defcallback(caller,prompt,user_input):
+ """
+ This is a callback you define yourself.
+
+ Args:
+ caller (Account or Object): The one being asked
+ for input
+ prompt (str): A copy of the current prompt
+ user_input (str): The input from the account.
+
+ Returns:
+ repeat (bool): If not set or False, exit the
+ input prompt and clean up. If returning anything
+ True, stay in the prompt, which means this callback
+ will be called again with the next user input.
+ """
+ caller.msg(f"When asked '{prompt}', you answered '{user_input}'.")
+
+get_input(caller,"Write something! ",callback)
+
Normally, the get_input function quits after any input, but as seen in the example docs, you could
+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
+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
+list.
Below is an example of a Yes/No prompt using the get_input function:
+
defyesno(caller,prompt,result):
+ ifresult.lower()in("y","yes","n","no"):
+ # do stuff to handle the yes/no answer
+ # ...
+ # if we return None/False the prompt state
+ # will quit after this
+ else:
+ # the answer is not on the right yes/no form
+ caller.msg("Please answer Yes or No. \n{prompt}")
+@# returning True will make sure the prompt state is not exited
+ returnTrue
+
+# ask the question
+get_input(caller,"Is Evennia great (Yes/No)?",yesno)
+
The evennia.utils.evmenu.list_node is an advanced decorator for use with EvMenu node functions.
+It is used to quickly create menus for manipulating large numbers of items.
The menu will automatically create an multi-page option listing that one can flip through. One can
+inpect each entry and then select them with prev/next. This is how it is used:
The options argument to list_node is either a list, a generator or a callable returning a list
+of strings for each option that should be displayed in the node.
+
The select is a callable in the example above but could also be the name of a menu node. If a
+callable, the menuchoice argument holds the selection done and available_choices holds all the
+options available. The callable should return the menu to go to depending on the selection (or
+None to rerun the same node). If the name of a menu node, the selection will be passed as
+selection kwarg to that node.
+
The decorated node itself should return text to display in the node. It must return at least an
+empty dictionary for its options. It returning options, those will supplement the options
+auto-created by the list_node decorator.
Sometimes you want to make a chain of menu nodes one after another, but you don’t want the user to
-be able to continue to the next node until you have verified that what they input in the previous
-node is ok. A common example is a login menu:
+
Sometimes you want to make a chain of menu nodes one after another, but you don’t want the user to be able to continue to the next node until you have verified that what they input in the previous node is ok. A common example is a login menu:
def_check_username(caller,raw_string,**kwargs):# we assume lookup_username() exists
@@ -1177,178 +1291,6 @@ probably more work than it’s worth: You can create dynamic menus by instead ma
function more clever. See the NPC shop tutorial for an example of this.
-
-
This describes two ways for asking for simple questions from the user. Using Python’s input
-will not work in Evennia. input will block the entire server for everyone until that one
-player has entered their text, which is not what you want.
In the func method of your Commands (only) you can use Python’s built-in yield command to
-request input in a similar way to input. It looks like this:
-
result=yield("Please enter your answer:")
-
-
-
This will send “Please enter your answer” to the Command’s self.caller and then pause at that
-point. All other players at the server will be unaffected. Once caller enteres a reply, the code
-execution will continue and you can do stuff with the result. Here is an example:
-
fromevenniaimportCommand
-classCmdTestInput(Command):
- key="test"
- deffunc(self):
- result=yield("Please enter something:")
- self.caller.msg(f"You entered {result}.")
- result2=yield("Now enter something else:")
- self.caller.msg(f"You now entered {result2}.")
-
-
-
Using yield is simple and intuitive, but it will only access input from self.caller and you
-cannot abort or time out the pause until the player has responded. Under the hood, it is actually
-just a wrapper calling get_input described in the following section.
-
-
Important Note: In Python you cannot mix yield and return<value> in the same method. It has
-to do with yield turning the method into a
-generator. A return without an argument works, you
-can just not do return<value>. This is usually not something you need to do in func() anyway,
-but worth keeping in mind.
The evmenu module offers a helper function named get_input. This is wrapped by the yield
-statement which is often easier and more intuitive to use. But get_input offers more flexibility
-and power if you need it. While in the same module as EvMenu, get_input is technically unrelated
-to it. The get_input allows you to ask and receive simple one-line input from the user without
-launching the full power of a menu to do so. To use, call get_input like this:
-
get_input(caller,prompt,callback)
-
-
-
Here caller is the entity that should receive the prompt for input given as prompt. The
-callback is a callable function(caller,prompt,user_input) that you define to handle the answer
-from the user. When run, the caller will see prompt appear on their screens and any text they
-enter will be sent into the callback for whatever processing you want.
-
Below is a fully explained callback and example call:
-
fromevenniaimportCommand
-fromevennia.utils.evmenuimportget_input
-
-defcallback(caller,prompt,user_input):
- """
- This is a callback you define yourself.
-
- Args:
- caller (Account or Object): The one being asked
- for input
- prompt (str): A copy of the current prompt
- user_input (str): The input from the account.
-
- Returns:
- repeat (bool): If not set or False, exit the
- input prompt and clean up. If returning anything
- True, stay in the prompt, which means this callback
- will be called again with the next user input.
- """
- caller.msg(f"When asked '{prompt}', you answered '{user_input}'.")
-
-get_input(caller,"Write something! ",callback)
-
Normally, the get_input function quits after any input, but as seen in the example docs, you could
-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
-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
-list.
Below is an example of a Yes/No prompt using the get_input function:
-
defyesno(caller,prompt,result):
- ifresult.lower()in("y","yes","n","no"):
- # do stuff to handle the yes/no answer
- # ...
- # if we return None/False the prompt state
- # will quit after this
- else:
- # the answer is not on the right yes/no form
- caller.msg("Please answer Yes or No. \n{prompt}")
-@# returning True will make sure the prompt state is not exited
- returnTrue
-
-# ask the question
-get_input(caller,"Is Evennia great (Yes/No)?",yesno)
-
The evennia.utils.evmenu.list_node is an advanced decorator for use with EvMenu node functions.
-It is used to quickly create menus for manipulating large numbers of items.
The menu will automatically create an multi-page option listing that one can flip through. One can
-inpect each entry and then select them with prev/next. This is how it is used:
The options argument to list_node is either a list, a generator or a callable returning a list
-of strings for each option that should be displayed in the node.
-
The select is a callable in the example above but could also be the name of a menu node. If a
-callable, the menuchoice argument holds the selection done and available_choices holds all the
-options available. The callable should return the menu to go to depending on the selection (or
-None to rerun the same node). If the name of a menu node, the selection will be passed as
-selection kwarg to that node.
-
The decorated node itself should return text to display in the node. It must return at least an
-empty dictionary for its options. It returning options, those will supplement the options
-auto-created by the list_node decorator.
The EvMenu is implemented using Commands. When you start a new EvMenu, the user of the
-menu will be assigned a CmdSet with the commands they need to navigate the menu.
-This means that if you were to, from inside the menu, assign a new command set to the caller, you
-may override the Menu Cmdset and kill the menu. If you want to assign cmdsets to the caller as part
-of the menu, you should store the cmdset on caller.ndb._evmenu and wait to actually assign it
-until the exit node.
@@ -110,8 +102,6 @@
window. The evennia.utils.evmore.EvMore class gives the user the in-game ability to only view one
page of text at a time. It is usually used via its access function, evmore.msg.
The name comes from the famous unix pager utility more which performs just this function.
The FuncParser extracts and executes ‘inline functions’ embedded in a string on the form $funcname(args,kwargs). Under the hood, this will lead to a call to a Python function you control. The inline function call will be replaced by the return from the function.
fromevennia.utils.funcparserimportFuncParser
@@ -147,32 +146,20 @@
"This is an escaped $pow(4) and so is this $pow(3)"
The FuncParser can be applied to any string. Out of the box it’s applied in a few situations:
-
Outgoing messages. All messages sent from the server is processed through FuncParser and every
-callable is provided the Session of the object receiving the message. This potentially
-allows a message to be modified on the fly to look different for different recipients.
-
Prototype values. A Prototype dict’s values are run through the parser such that every
-callable gets a reference to the rest of the prototype. In the Prototype ORM, this would allow builders
-to safely call functions to set non-string values to prototype values, get random values, reference
+
Outgoing messages. All messages sent from the server is processed through FuncParser and every callable is provided the Session of the object receiving the message. This potentially allows a message to be modified on the fly to look different for different recipients.
+
Prototype values. A Prototype dict’s values are run through the parser such that every callable gets a reference to the rest of the prototype. In the Prototype ORM, this would allow builders to safely call functions to set non-string values to prototype values, get random values, reference
other fields of the prototype, and more.
-
Actor-stance in messages to others. In the
-Object.msg_contents method,
-the outgoing string is parsed for special $You() and $conj() callables to decide if a given recipient
+
Actor-stance in messages to others. In the Object.msg_contents method, the outgoing string is parsed for special $You() and $conj() callables to decide if a given recipient
should see “You” or the character’s name.
Important
-
The inline-function parser is not intended as a ‘softcode’ programming language. It does not
-have things like loops and conditionals, for example. While you could in principle extend it to
-do very advanced things and allow builders a lot of power, all-out coding is something
-Evennia expects you to do in a proper text editor, outside of the game, not from inside it.
+
The inline-function parser is not intended as a ‘softcode’ programming language. It does not have things like loops and conditionals, for example. While you could in principle extend it to do very advanced things and allow builders a lot of power, all-out coding is something Evennia expects you to do in a proper text editor, outside of the game, not from inside it.
You can apply inline function parsing to any string. The
FuncParser is imported as evennia.utils.funcparser.
fromevennia.utilsimportfuncparser
@@ -237,8 +224,7 @@ is the raise_errors
funcparser=<FuncParser>,raise_errors=False)
-
The mydefault=2 kwarg could be overwritten if we made the call as $test(mydefault=...)
-but myreserved=[1,2,3] will always be sent as-is and will override a call $test(myreserved=...).
+
The mydefault=2 kwarg could be overwritten if we made the call as $test(mydefault=...) but myreserved=[1,2,3] will always be sent as-is and will override a call $test(myreserved=...).
The funcparser/raise_errors kwargs are also always included as reserved kwargs.
@@ -250,20 +236,15 @@ The funcparser
-
The *args and **kwargs must always be included. If you are unsure how *args and **kwargs work in Python,
-read about them here.
+
The *args and **kwargs must always be included. If you are unsure how *args and **kwargs work in Python, read about them here.
The input from the innermost $funcname(...) call in your callable will always be a str. Here’s
an example of an $toint function; it converts numbers to integers.
"There's a $toint(22.0)% chance of survival."
-
What will enter the $toint callable (as args[0]) is the string"22.0". The function is responsible
-for converting this to a number so that we can convert it to an integer. We must also properly handle invalid
-inputs (like non-numbers).
-
If you want to mark an error, raise evennia.utils.funcparser.ParsingError. This stops the entire parsing
-of the string and may or may not raise the exception depending on what you set raise_errors to when you
-created the parser.
+
What will enter the $toint callable (as args[0]) is the string"22.0". The function is responsible for converting this to a number so that we can convert it to an integer. We must also properly handle invalid inputs (like non-numbers).
+
If you want to mark an error, raise evennia.utils.funcparser.ParsingError. This stops the entire parsing of the string and may or may not raise the exception depending on what you set raise_errors to when you created the parser.
However, if you nest functions, the return of the innermost function may be something other than
a string. Let’s introduce the $eval function, which evaluates simple expressions using
Python’s literal_eval and/or simple_eval. It returns whatever data type it
@@ -275,18 +256,14 @@ evaluates to.
It evaluates this and returns the float22.0. This time the outermost $toint will be called with
this float instead of with a string.
-
It’s important to safely validate your inputs since users may end up nesting your callables in any order.
-See the next section for useful tools to help with this.
+
It’s important to safely validate your inputs since users may end up nesting your callables in any order. See the next section for useful tools to help with this.
-
In these examples, the result will be embedded in the larger string, so the result of the entire parsing
-will be a string:
+
In these examples, the result will be embedded in the larger string, so the result of the entire parsing will be a string:
parser.parse(above_string)"There's a 22% chance of survival."
-
However, if you use the parse_to_any (or parse(...,return_str=False)) and
-don’t add any extra string around the outermost function call,
-you’ll get the return type of the outermost callable back:
+
However, if you use the parse_to_any (or parse(...,return_str=False)) and don’t add any extra string around the outermost function call, you’ll get the return type of the outermost callable back:
parser.parse_to_any("$toint($eval(10 * 2.2)")22parser.parse_to_any("the number $toint($eval(10 * 2.2).")
@@ -342,9 +319,7 @@ can support. This is because FunctionParser strings can be used by
non-developer players/builders and some things (such as complex
classes/callables etc) are just not safe/possible to convert from string
representation.
-
In evennia.utils.utils is a helper called
-safe_convert_to_types. This function
-automates the conversion of simple data types in a safe way:
+
In evennia.utils.utils is a helper called safe_convert_to_types. This function automates the conversion of simple data types in a safe way:
fromevennia.utils.utilsimportsafe_convert_to_typesdef_process_callable(*args,**kwargs):
@@ -360,43 +335,24 @@ automates the conversion of simple data types in a safe way:
-
In other words, in the callable $process(expression,local,extra1=..,extra2=...), the first argument will be handled by the ‘py’ converter
-(described below), the second will passed through regular Python str,
-kwargs will be handled by int and str respectively. You can supply
-your own converter function as long as it takes one argument and returns
-the converted result.
-
In other words,
+
In other words, in the callable $process(expression,local,extra1=..,extra2=...), the first argument will be handled by the ‘py’ converter (described below), the second will passed through regular Python str, kwargs will be handled by int and str respectively. You can supply your own converter function as long as it takes one argument and returns the converted result.
The special converter "py" will try to convert a string argument to a Python structure with the help of the
-following tools (which you may also find useful to experiment with on your own):
+
The special converter "py" will try to convert a string argument to a Python structure with the help of the following tools (which you may also find useful to experiment with on your own):
-
ast.literal_eval is an in-built Python
-function. It
-only supports strings, bytes, numbers, tuples, lists, dicts, sets, booleans and None. That’s
-it - no arithmetic or modifications of data is allowed. This is good for converting individual values and
-lists/dicts from the input line to real Python objects.
-
simpleeval is a third-party tool included with Evennia. This
-allows for evaluation of simple (and thus safe) expressions. One can operate on numbers and strings
-with +-/* as well as do simple comparisons like 4>3 and more. It does not accept more complex
-containers like lists/dicts etc, so this and literal_eval are complementary to each other.
+
ast.literal_eval is an in-built Python function. It only supports strings, bytes, numbers, tuples, lists, dicts, sets, booleans and None. That’s it - no arithmetic or modifications of data is allowed. This is good for converting individual values and lists/dicts from the input line to real Python objects.
+
simpleeval is a third-party tool included with Evennia. This allows for evaluation of simple (and thus safe) expressions. One can operate on numbers and strings with +-/* as well as do simple comparisons like 4>3 and more. It does not accept more complex containers like lists/dicts etc, so this and literal_eval are complementary to each other.
Warning
-
It may be tempting to run use Python’s in-built eval() or exec() functions as converters since
-these are able to convert any valid Python source code to Python. NEVER DO THIS unless you really, really
-know that ONLY developers will ever modify the string going into the callable. The parser is intended
-for untrusted users (if you were trusted you’d have access to Python already). Letting untrusted users
-pass strings to eval/exec is a MAJOR security risk. It allows the caller to run arbitrary
-Python code on your server. This is the path to maliciously deleted hard drives. Just don’t do it and
-sleep better at night.
+
It may be tempting to run use Python’s in-built eval() or exec() functions as converters since these are able to convert any valid Python source code to Python. NEVER DO THIS unless you really, really know that ONLY developers will ever modify the string going into the callable. The parser is intended for untrusted users (if you were trusted you’d have access to Python already). Letting untrusted users pass strings to eval/exec is a MAJOR security risk. It allows the caller to run arbitrary Python code on your server. This is the path to maliciously deleted hard drives. Just don’t do it and sleep better at night.
These are some example callables you can import and add your parser. They are divided into
global-level dicts in evennia.utils.funcparser. Just import the dict(s) and merge/add one or
more to them when you create your FuncParser instance to have those callables be available.
@@ -404,47 +360,28 @@ more to them when you create your evennia.utils.funcparser.FUNCPARSER_CALLABLES¶
These are the ‘base’ callables.
-
$eval(expression) (code) -
-this uses literal_eval and simple_eval (see previous section) attemt to convert a string expression
-to a python object. This handles e.g. lists of literals [1,2,3] and simple expressions like "1+2".
-
$toint(number) (code) -
-always converts an output to an integer, if possible.
+
$eval(expression) (code) - this uses literal_eval and simple_eval (see previous section) attemt to convert a string expression to a python object. This handles e.g. lists of literals [1,2,3] and simple expressions like "1+2".
+
$toint(number) (code) - always converts an output to an integer, if possible.
$add/sub/mult/div(obj1,obj2) (code) -
-this adds/subtracts/multiplies and divides to elements together. While simple addition could be done with
-$eval, this could for example be used also to add two lists together, which is not possible with eval;
-for example $add($eval([1,2,3]),$eval([4,5,6]))->[1,2,3,4,5,6].
-
$round(float,significant) (code) -
-rounds an input float into the number of provided significant digits. For example $round(3.54343,3)->3.543.
-
$random([start,[end]]) (code) -
-this works like the Python random() function, but will randomize to an integer value if both start/end are
+this adds/subtracts/multiplies and divides to elements together. While simple addition could be done with $eval, this could for example be used also to add two lists together, which is not possible with eval; for example $add($eval([1,2,3]),$eval([4,5,6]))->[1,2,3,4,5,6].
+
$round(float,significant) (code) - rounds an input float into the number of provided significant digits. For example $round(3.54343,3)->3.543.
+
$random([start,[end]]) (code) - this works like the Python random() function, but will randomize to an integer value if both start/end are
integers. Without argument, will return a float between 0 and 1.
-
$randint([start,[end]]) (code) -
-works like the randint() python function and always returns an integer.
-
$choice(list) (code) -
-the input will automatically be parsed the same way as $eval and is expected to be an iterable. A random
-element of this list will be returned.
-
$pad(text[,width,align,fillchar]) (code) -
-this will pad content. $pad("Hello",30,c,-) will lead to a text centered in a 30-wide block surrounded by -
-characters.
-
$crop(text,width=78,suffix='[...]') (code) -
-this will crop a text longer than the width, by default ending it with a [...]-suffix that also fits within
-the width. If no width is given, the client width or settings.DEFAULT_CLIENT_WIDTH will be used.
-
$space(num) (code) -
-this will insert num spaces.
-
$just(string,width=40,align=c,indent=2) (code) -
-justifies the text to a given width, aligning it left/right/center or ‘f’ for full (spread text across width).
+
$randint([start,[end]]) (code) - works like the randint() python function and always returns an integer.
+
$choice(list) (code) - the input will automatically be parsed the same way as $eval and is expected to be an iterable. A random element of this list will be returned.
+
$pad(text[,width,align,fillchar]) (code) - this will pad content. $pad("Hello",30,c,-) will lead to a text centered in a 30-wide block surrounded by - characters.
+
$crop(text,width=78,suffix='[...]') (code) - this will crop a text longer than the width, by default ending it with a [...]-suffix that also fits within the width. If no width is given, the client width or settings.DEFAULT_CLIENT_WIDTH will be used.
$just(string,width=40,align=c,indent=2) (code) - justifies the text to a given width, aligning it left/right/center or ‘f’ for full (spread text across width).
$ljust - shortcut to justify-left. Takes all other kwarg of $just.
$rjust - shortcut to right justify.
$cjust - shortcut to center justify.
-
$clr(startcolor,text[,endcolor]) (code) -
-color text. The color is given with one or two characters without the preceeding |. If no endcolor is
-given, the string will go back to neutral, so $clr(r,Hello) is equivalent to |rHello|n.
+
$clr(startcolor,text[,endcolor]) (code) - color text. The color is given with one or two characters without the preceeding |. If no endcolor is given, the string will go back to neutral, so $clr(r,Hello) is equivalent to |rHello|n.
These are callables that requires access-checks in order to search for objects. So they require some
-extra reserved kwargs to be passed when running the parser:
+
These are callables that requires access-checks in order to search for objects. So they require some extra reserved kwargs to be passed when running the parser:
parser.parse_to_any(string,caller=<objectoraccount>,access="control",...)
@@ -453,27 +390,20 @@ extra reserved kwargs to be passed when running the parser:
The caller is required, it’s the the object to do the access-check for. The access kwarg is the
lock type to check, default being "control".
-
$search(query,type=account|script,return_list=False) (code) -
-this will look up and try to match an object by key or alias. Use the type kwarg to
-search for account or script instead. By default this will return nothing if there are more than one
-match; if return_list is True a list of 0, 1 or more matches will be returned instead.
+
$search(query,type=account|script,return_list=False) (code) - this will look up and try to match an object by key or alias. Use the type kwarg to search for account or script instead. By default this will return nothing if there are more than one match; if return_list is True a list of 0, 1 or more matches will be returned instead.
$obj(query), $dbref(query) - legacy aliases for $search.
$objlist(query) - legacy alias for $search, always returning a list.
These are used to implement actor-stance emoting. They are used by the
-DefaultObject.msg_contents method
-by default. You can read a lot more about this on the page
+
Here the caller is the one sending the message and receiver the one to see it. The mapping contains
-references to other objects accessible via these callables.
+
Here the caller is the one sending the message and receiver the one to see it. The mapping contains references to other objects accessible via these callables.
$you([key]) (code) -
if no key is given, this represents the caller, otherwise an object from mapping
@@ -555,7 +485,7 @@ all the defaults (like Evennia 1.0-dev »
Evennia has an extensive help system covering both command-help and regular
-free-form help documentation. It supports subtopics and if failing to find a
-match it will provide suggestsions, first from alternative topics and then by
-finding mentions of the search term in help entries.
------------------------------------------------------------------------------
+Help for The theatre (aliases: the hub, curtains)
-Thetheatreisatthecentreofthecity,bothliterallyandfiguratively...
-(Alotmoretextaboutitfollows...)
+The theatre is at the centre of the city, both literally and figuratively ...
+(A lot more text about it follows ...)
-Subtopics:
- theatre/lore
- theatre/layout
- theatre/dramatispersonae
-------------------------------------------------------------------------------
+Subtopics:
+ theatre/lore
+ theatre/layout
+ theatre/dramatis personae
+------------------------------------------------------------------------------
------------------------------------------------------------------------------
+No help found
-Thereisnohelptopicmatching'evennia'.
-...Butmatcheswherefoundwithinthehelptextsofthesuggestionsbelow.
+There is no help topic matching 'evennia'.
+... But matches where found within the help texts of the suggestions below.
-Suggestions:
- grapevine2chan,about,irc2chan
------------------------------------------------------------------------------
+Suggestions:
+ grapevine2chan, about, irc2chan
+-----------------------------------------------------------------------------
Evennia has an extensive help system covering both command-help and regular free-form help documentation. It supports subtopics and if failing to find a match it will provide suggestsions, first from alternative topics and then by finding mentions of the search term in help entries.
The help system is accessed in-game by use of the help command:
help <topic>
Sub-topics are accessed as help<topic>/<subtopic>/....
@@ -169,194 +175,22 @@ finding mentions of the search term in help entries.
sethelp The Gods;pantheon, Lore = In the beginning all was dark ...
-
Use the /edit switch to open the EvEditor for more convenient in-game writing
-(but note that devs can also create help entries outside the game using their
-regular code editor, see below).
-
-
You can also create help entries as Python modules, outside of the game. See
-FileHelp entries below.
Auto-generated command help - this is literally the doc-strings of
-the Command classes. The idea is that the command docs are
-easier to maintain and keep up-to-date if the developer can change them at the
-same time as they do the code.
-
Database-stored help entries - These are created in-game (using the
-default sethelp command as exemplified in the previous section).
-
File-stored help entries - These are created outside the game, as dicts in
-normal Python modules. They allows developers to write and maintain their help
-files using a proper text editor.
All help entries (no matter the source) have the following properties:
-
-
key - This is the main topic-name. For Commands, this is literally the
-command’s key.
-
aliases - Alternate names for the help entry. This can be useful if the main
-name is hard to remember.
-
help_category - The general grouping of the entry. This is optional. If not
-given it will use the default category given by
-settings.COMMAND_DEFAULT_HELP_CATEGORY for Commands and
-settings.DEFAULT_HELP_CATEGORY for file+db help entries.
-
locks - Lock string (for commands) or LockHandler (all help entries).
-This defines who may read this entry. See the next section.
-
tags - This is not used by default, but could be used to further organize
-help entries.
-
text - The actual help entry text. This will be dedented and stripped of
-extra space at beginning and end.
-
-
A text that scrolls off the screen will automatically be paginated by
-the EvMore pager (you can control this with
-settings.HELP_MORE_ENABLED=False). If you use EvMore and want to control
-exactly where the pager should break the page, mark the break with the control
-character \f.
Rather than making a very long help entry, the text may also be broken up
-into subtopics. A list of the next level of subtopics are shown below the
-main help text and allows the user to read more about some particular detail
-that wouldn’t fit in the main text.
-
Subtopics use a markup slightly similar to markdown headings. The top level
-heading must be named #subtopics (non case-sensitive) and the following
-headers must be sub-headings to this (so ##subtopicname etc). All headings
-are non-case sensitive (the help command will format them). The topics can be
-nested at most to a depth of 5 (which is probably too many levels already). The
-parser uses fuzzy matching to find the subtopic, so one does not have to type
-it all out exactly.
-
Below is an example of a text with sub topics.
-
The theatre is the heart of the city, here you can find ...
-(This is the main help text, what you get with `help theatre`)
-
-# subtopics
-
-## lore
-
-The theatre holds many mysterious things...
-(`help theatre/lore`)
-
-### the grand opening
-
-The grand opening is the name for a mysterious event where ghosts appeared ...
-(`this is a subsub-topic to lore, accessible as `help theatre/lore/grand` or
-any other partial match).
-
-### the Phantom
-
-Deep under the theatre, rumors has it a monster hides ...
-(another subsubtopic, accessible as `help theatre/lore/phantom`)
-
-## layout
-
-The theatre is a two-story building situated at ...
-(`help theatre/layout`)
-
-## dramatis personae
-
-There are many interesting people prowling the halls of the theatre ...
-(`help theatre/dramatis` or `help theathre/drama` or `help theatre/personae` would work)
-
-### Primadonna Ada
-
-Everyone knows the primadonna! She is ...
-(A subtopic under dramatis personae, accessible as `help theatre/drama/ada` etc)
-
-### The gatekeeper
-
-He always keeps an eye on the door and ...
-(`help theatre/drama/gate`)
-
-
The auto-help system uses the __doc__ strings of your command classes and
-formats this to a nice- looking help entry. This makes for a very easy way to
-keep the help updated - just document your commands well and updating the help
-file is just a reload away.
-
Example (from a module with command definitions):
-
classCmdMyCmd(Command):
- """
- mycmd - my very own command
-
- Usage:
- mycmd[/switches] <args>
-
- Switches:
- test - test the command
- run - do something else
-
- This is my own command that does this and that.
-
- """
- # [...]
-
- locks="cmd:all();read:all()"# default
- help_category="General"# default
- auto_help=True# default
-
- # [...]
-
-
-
The text at the very top of the command class definition is the class’
-__doc__-string and will be shown to users looking for help. Try to use a
-consistent format - all default commands are using the structure shown above.
-
You can limit access to the help entry by the view and/or read locks on the
-Command. See the section below for details.
-
You should also supply the help_category class property if you can; this helps
-to group help entries together for people to more easily find them. See the
-help command in-game to see the default categories. If you don’t specify the
-category, settings.COMMAND_DEFAULT_HELP_CATEGORY (default is “General”) is
-used.
-
If you don’t want your command to be picked up by the auto-help system at all
-(like if you want to write its docs manually using the info in the next section
-or you use a cmdset that has its own help functionality) you
-can explicitly set auto_help class property to False in your command
-definition.
-
Alternatively, you can keep the advantages of auto-help in commands, but
-control the display of command helps. You can do so by overriding the command’s
-get_help(caller,cmdset) method. By default, this method will return the
-class docstring. You could modify it to add custom behavior: the text returned
-by this method will be displayed to the character asking for help in this
-command.
These are most commonly created in-game using the sethelp command. If you need to create one
-manually, you can do so with evennia.create_help_entry():
-
-fromevenniaimportcreate_help_entry
+
This will create a new help entry in the database. Use the /edit switch to open the EvEditor for more convenient in-game writing (but note that devs can also create help entries outside the game using their regular code editor, see below).
+
The HelpEntry stores database help. It is not a Typeclassed entity and can’t be extended using the typeclass mechanism.
+
Here’s how to create a database-help entry in code:
+
fromevenniaimportcreate_help_entryentry=create_help_entry("emote","Emoting is important because ...",category="Roleplaying",locks="view:all()")
-
The entity being created is a evennia.help.models.HelpEntry
-object. This is not a Typeclassed entity and is not meant to
-be modified to any great degree. It holds the properties listed earlier. The
-text is stored in a field entrytext. It does not provide a get_help method
-like commands, stores and returns the entrytext directly.
-
You can search for (db-)-HelpEntry objects using evennia.search_help but note that
-this will not return the two other types of help entries.
File-help entries are created by the game development team outside of the game. The
-help entries are defined in normal Python modules (.py file ending) containing
-a dict to represent each entry. They require a server reload before any changes
-apply.
+
File-help entries are created by the game development team outside of the game. The help entries are defined in normal Python modules (.py file ending) containing a dict to represent each entry. They require a server reload before any changes apply.
Evennia will look through all modules given by
settings.FILE_HELP_ENTRY_MODULES. This should be a list of python-paths for
@@ -411,26 +245,36 @@ Here’s an example of a help module:
to keep your strings a reasonable width (it will look better). Just reload the
server and the file-based help entries will be available to view.
The __docstring__ of Command classes are automatically extracted into a help entry. You set help_category directly on the class.
+
fromevenniaimportCommand
+
+classMyCommand(Command):
+ """
+ This command is great!
+
+ Usage:
+ mycommand [argument]
+
+ When this command is called, great things happen. If you
+ pass an argument, even GREATER things HAPPEN!
+
+ """
+
+ key="mycommand"
+
+ locks:"cmd:all();read:all()"# default
+ help_category="General"# default
+ auto_help=True# default
+
+ # ...
-
So if you create a db-help entry ‘foo’, it will replace any file-based help
-entry ‘foo’. But if there is a Command ‘foo’, that’s the help you’ll get when
-you enter helpfoo.
-
The reasoning for this is that commands must always be understood in order to
-play the game. Meanwhile db-based help can be kept up-to-date from in-game
-builders and may be less ‘static’ than the file-based ones.
-
The sethelp command (which only deals with creating db-based help entries)
-will warn you if a new help entry might shadow/be shadowed by a
-same/similar-named command or file-based help entry.
+
When you update your code, the command’s help will follow. The idea is that the command docs are easier to maintain and keep up-to-date if the developer can change them at the same time as they do the code.
The default help command gather all available commands and help entries
together so they can be searched or listed. By setting locks on the command/help
entry one can limit who can read help about it.
@@ -473,32 +317,100 @@ the new ‘read’ lock-type to control access to the entry itself.
Since the available commands may vary from moment to moment, help is
-responsible for collating the three sources of help-entries (commands/db/file)
-together and search through them on the fly. It also does all the formatting of
-the output.
-
To make it easier to tweak the look, the parts of the code that changes the
-visual presentation and entity searching has been broken out into separate
-methods on the command class. Override these in your version of help to change
-the display or tweak as you please. See the api link above for details.
Since the available commands may vary from moment to moment, help is responsible for collating the three sources of help-entries (commands/db/file) together and search through them on the fly. It also does all the formatting of the output.
+
To make it easier to tweak the look, the parts of the code that changes the visual presentation and entity searching has been broken out into separate methods on the command class. Override these in your version of help to change the display or tweak as you please. See the api link above for details.
Rather than making a very long help entry, the text may also be broken up into subtopics. A list of the next level of subtopics are shown below the main help text and allows the user to read more about some particular detail that wouldn’t fit in the main text.
+
Subtopics use a markup slightly similar to markdown headings. The top level heading must be named #subtopics (non case-sensitive) and the following headers must be sub-headings to this (so ##subtopicname etc). All headings are non-case sensitive (the help command will format them). The topics can be nested at most to a depth of 5 (which is probably too many levels already). The parser uses fuzzy matching to find the subtopic, so one does not have to type it all out exactly.
+
Below is an example of a text with sub topics.
+
The theatre is the heart of the city, here you can find ...
+(This is the main help text, what you get with `help theatre`)
+
+# subtopics
+
+## lore
+
+The theatre holds many mysterious things...
+(`help theatre/lore`)
+
+### the grand opening
+
+The grand opening is the name for a mysterious event where ghosts appeared ...
+(`this is a subsub-topic to lore, accessible as `help theatre/lore/grand` or
+any other partial match).
+
+### the Phantom
+
+Deep under the theatre, rumors has it a monster hides ...
+(another subsubtopic, accessible as `help theatre/lore/phantom`)
+
+## layout
+
+The theatre is a two-story building situated at ...
+(`help theatre/layout`)
+
+## dramatis personae
+
+There are many interesting people prowling the halls of the theatre ...
+(`help theatre/dramatis` or `help theathre/drama` or `help theatre/personae` would work)
+
+### Primadonna Ada
+
+Everyone knows the primadonna! She is ...
+(A subtopic under dramatis personae, accessible as `help theatre/drama/ada` etc)
+
+### The gatekeeper
+
+He always keeps an eye on the door and ...
+(`help theatre/drama/gate`)
+
+
Since it needs to search so different types of data, the help system has to
-collect all possibilities in memory before searching through the entire set. It
-uses the Lunr search engine to
-search through the main bulk of help entries. Lunr is a mature engine used for
-web-pages and produces much more sensible results than previous solutions.
-
Once the main entry has been found, subtopics are then searched with
-simple ==, startswith and in matching (there are so relatively few of them
-at that point).
Should you have clashing help-entries (of the same name) between the three types of available entries, the priority is
+
Command-auto-help > Db-help > File-help
+
+
+
The sethelp command (which only deals with creating db-based help entries) will warn you if a new help entry might shadow/be shadowed by a same/similar-named command or file-based help entry.
All help entries (no matter the source) are parsed into an object with the following properties:
+
+
key - This is the main topic-name. For Commands, this is literally the command’s key.
+
aliases - Alternate names for the help entry. This can be useful if the main name is hard to remember.
+
help_category - The general grouping of the entry. This is optional. If not given it will use the default category given by settings.COMMAND_DEFAULT_HELP_CATEGORY for Commands and
+settings.DEFAULT_HELP_CATEGORY for file+db help entries.
+
locks - Lock string (for commands) or LockHandler (all help entries). This defines who may read this entry. See the next section.
+
tags - This is not used by default, but could be used to further organize help entries.
+
text - The actual help entry text. This will be dedented and stripped of extra space at beginning and end.
A text that scrolls off the screen will automatically be paginated by the EvMore pager (you can control this with settings.HELP_MORE_ENABLED=False). If you use EvMore and want to control exactly where the pager should break the page, mark the break with the control character \f.
Since it needs to search so different types of data, the help system has to collect all possibilities in memory before searching through the entire set. It uses the Lunr search engine to search through the main bulk of help entries. Lunr is a mature engine used for web-pages and produces much more sensible results than previous solutions.
+
Once the main entry has been found, subtopics are then searched with simple ==, startswith and in matching (there are so relatively few of them at that point).
Changed in version 1.0: Replaced the old bag-of-words algorithm with lunr package.
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
The Inputfunc is the last fixed step on the Ingoing message path. The available Inputfuncs are looked up and called using commandtuple structures sent from the client. The job of the Inputfunc is to perform whatever action is requested, by firing a Command, performing a database query or whatever is needed.
+
Given a commandtuple on the form
+
(commandname, (args), {kwargs})
+
+
+
Evennia will try to find and call an Inputfunc on the form
+
defcommandname(session,*args,**kwargs):
+ # ...
Or, if no match was found, it will call an inputfunc named “default” on this form
-
defdefault(session,cmdname,*args,**kwargs):
- # cmdname is the name of the mismatched inputcommand
+
defdefault(session,cmdname,*args,**kwargs):
+ # cmdname is the name of the mismatched inputcommand
This is simple. Add a function on the above form to mygame/server/conf/inputfuncs.py. Your
-function must be in the global, outermost scope of that module and not start with an underscore
-(_) to be recognized as an inputfunc. Reload the server. That’s it. To overload a default
-inputfunc (see below), just add a function with the same name.
-
The modules Evennia looks into for inputfuncs are defined in the list settings.INPUT_FUNC_MODULES.
-This list will be imported from left to right and later imported functions will replace earlier
-ones.
+
+
Add a function on the above form to mygame/server/conf/inputfuncs.py. Your function must be in the global, outermost scope of that module and not start with an underscore (_) to be recognized as an inputfunc. i
+
reload the server.
+
+
To overload a default inputfunc (see below), just add a function with the same name. You can also extend the settings-list INPUT_FUNC_MODULES.
All global-level functions with a name not starting with _ in these module(s) will be used by Evennia as an inputfunc. The list is imported from left to right, so latter imported functions will replace earlier ones.
This is the most common of inputcommands, and the only one supported by every traditional mud. The
-argument is usually what the user sent from their command line. Since all text input from the user
-like this is considered a Command, this inputfunc will do things like nick-replacement
-and then pass on the input to the central Commandhandler.
+
This is the most common of inputs, and the only one supported by every traditional mud. The argument is usually what the user sent from their command line. Since all text input from the user
+like this is considered a Command, this inputfunc will do things like nick-replacement and then pass on the input to the central Commandhandler.
Note that there are two GMCP aliases to this inputfunc - hello and supports_set, which means
-it will be accessed via the GMCP Hello and Supports.Set instructions assumed by some clients.
+
Note that there are two GMCP aliases to this inputfunc - hello and supports_set, which means it will be accessed via the GMCP Hello and Supports.Set instructions assumed by some clients.
@@ -213,8 +224,7 @@ it will be accessed via the GMCP
Input: ("get_client_options,(),{key:value,...})
Output: ("client_options,(),{key:value,...})
-
This is a convenience wrapper that retrieves the current options by sending “get” to
-client_options above.
+
This is a convenience wrapper that retrieves the current options by sending “get” to client_options above.
Returns an outputcommand on the form ("get_inputfuncs",(),{funcname:docstring,...}) - a list of
-all the available inputfunctions along with their docstrings.
+
Returns an outputcommand on the form ("get_inputfuncs",(),{funcname:docstring,...}) - a list of all the available inputfunctions along with their docstrings.
Retrieves a value from the Character or Account currently controlled by this Session. Takes one
-argument, This will only accept particular white-listed names, you’ll need to overload the function
-to expand. By default the following values can be retrieved:
+
Retrieves a value from the Character or Account currently controlled by this Session. Takes one argument, This will only accept particular white-listed names, you’ll need to overload the function to expand. By default the following values can be retrieved:
“name” or “key”: The key of the Account or puppeted Character.
“location”: Name of the current location, or “None”.
@@ -252,16 +259,11 @@ to expand. By default the following values can be retrieved:
Output: Depends on the repeated function. Will return ("text",(repeatlist),{} with a list of
accepted names if given an unfamiliar callback name.
-
This will tell evennia to repeatedly call a named function at a given interval. Behind the scenes
-this will set up a Ticker. Only previously acceptable functions are possible to
-repeat-call in this way, you’ll need to overload this inputfunc to add the ones you want to offer.
-By default only two example functions are allowed, “test1” and “test2”, which will just echo a text
-back at the given interval. Stop the repeat by sending "stop":True (note that you must include
-both the callback name and interval for Evennia to know what to stop).
+
This will tell evennia to repeatedly call a named function at a given interval. Behind the scenes this will set up a Ticker. Only previously acceptable functions are possible to repeat-call in this way, you’ll need to overload this inputfunc to add the ones you want to offer. By default only two example functions are allowed, “test1” and “test2”, which will just echo a text back at the given interval. Stop the repeat by sending "stop":True (note that you must include both the callback name and interval for Evennia to know what to stop).
Output (on change): ("monitor",(),{"name":name,"value":value})
-
This sets up on-object monitoring of Attributes or database fields. Whenever the field or Attribute
-changes in any way, the outputcommand will be sent. This is using the
-MonitorHandler behind the scenes. Pass the “stop” key to stop monitoring. Note
-that you must supply the name also when stopping to let the system know which monitor should be
-cancelled.
-
Only fields/attributes in a whitelist are allowed to be used, you have to overload this function to
-add more. By default the following fields/attributes can be monitored:
+
This sets up on-object monitoring of Attributes or database fields. Whenever the field or Attribute changes in any way, the outputcommand will be sent. This is using the MonitorHandler behind the scenes. Pass the “stop” key to stop monitoring. Note that you must supply the name also when stopping to let the system know which monitor should be cancelled.
+
Only fields/attributes in a whitelist are allowed to be used, you have to overload this function to add more. By default the following fields/attributes can be monitored:
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.
+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.
Defining a lock (i.e. an access restriction) in Evennia is done by adding simple strings of lock
definitions to the object’s locks property using obj.locks.add().
Here are some examples of lock strings (not including the quotes):
@@ -181,6 +179,7 @@ the lockstring. The string below yields the same result as the previous example:
delete:id(34);edit:all();get: not attr(very_weak) or perm(Admin)
An access_type, the first part of a lockstring, defines what kind of capability a lock controls,
@@ -198,13 +197,8 @@ the default command set) actually checks for, as in the example of Objects:
-
control - who is the “owner” of the object. Can set locks, delete it etc. Defaults to the
-creator of the object.
-
call - who may call Object-commands stored on this Object except for the Object itself. By
-default, Objects share their Commands with anyone in the same location (e.g. so you can ‘press’ a
-Button object in the room). For Characters and Mobs (who likely only use those Commands for
-themselves and don’t want to share them) this should usually be turned off completely, using
-something like call:false().
+
control - who is the “owner” of the object. Can set locks, delete it etc. Defaults to the creator of the object.
+
call - who may call Object-commands stored on this Object except for the Object itself. By default, Objects share their Commands with anyone in the same location (e.g. so you can ‘press’ a Button object in the room). For Characters and Mobs (who likely only use those Commands for themselves and don’t want to share them) this should usually be turned off completely, using something like call:false().
examine - who may examine this object’s properties.
delete - who may delete the object.
edit - who may edit properties and attributes of the object.
@@ -264,14 +258,11 @@ boot listeners etc.
-
So to take an example, whenever an exit is to be traversed, a lock of the type traverse will be
-checked. Defining a suitable lock type for an exit object would thus involve a lockstring traverse:<lockfunctions>.
+
So to take an example, whenever an exit is to be traversed, a lock of the type traverse will be checked. Defining a suitable lock type for an exit object would thus involve a lockstring traverse:<lockfunctions>.
As stated above, the access_type part of the lock is simply the ‘name’ or ‘type’ of the lock. The
-text is an arbitrary string that must be unique for an object. If adding a lock with the same
-access_type as one that already exists on the object, the new one override the old one.
+
As stated above, the access_type part of the lock is simply the ‘name’ or ‘type’ of the lock. The text is an arbitrary string that must be unique for an object. If adding a lock with the same access_type as one that already exists on the object, the new one override the old one.
For example, if you wanted to create a bulletin board system and wanted to restrict who can either
read a board or post to a board. You could then define locks such as:
obj.locks.add("read:perm(Player);post:perm(Admin)")
@@ -290,7 +281,7 @@ trying to read the board):
A lock function is a normal Python function put in a place Evennia looks for such functions. The
+
A lock function is a normal Python function put in a place Evennia looks for such functions. The
modules Evennia looks at is the list settings.LOCK_FUNC_MODULES. All functions in any of those
modules will automatically be considered a valid lock function. The default ones are found in
evennia/locks/lockfuncs.py and you can start adding your own in mygame/server/conf/lockfuncs.py.
@@ -352,9 +343,8 @@ permissions and dbrefs of Accounts, not on Characters.
Sometimes you don’t really need to look up a certain lock, you just want to check a lockstring. A
common use is inside Commands, in order to check if a user has a certain permission. The lockhandler
has a method check_lockstring(accessing_obj,lockstring,bypass_superuser=False) that allows this.
@@ -364,11 +354,10 @@ has a method check_
return
-
Note here that the access_type can be left to a dummy value since this method does not actually do
-a Lock lookup.
+
Note here that the access_type can be left to a dummy value since this method does not actually do a Lock lookup.
Evennia sets up a few basic locks on all new objects and accounts (if we didn’t, noone would have
any access to anything from the start). This is all defined in the root Typeclasses
of the respective entity, in the hook method basetype_setup() (which you usually don’t want to
@@ -378,6 +367,7 @@ child object to change the default. Also creation commands like control lock_type so as to allow you, its creator, to
control and delete the object.
examine: attr(eyesight, excellent) or perm(Builders)
@@ -414,9 +404,8 @@ would be be added as a lock string to the create command sets up new objects. In sequence, this permission string sets the
owner of this object be the creator (the one running create). Builders may examine the object
whereas only Admins and the creator may delete it. Everyone can pick it up.
-
-
Django also implements a comprehensive permission/security system of its own. The reason we don’t
-use that is because it is app-centric (app in the Django sense). Its permission strings are of the
-form appname.permstring and it automatically adds three of them for each database model in the app
-
-
for the app evennia/object this would be for example ‘object.create’, ‘object.admin’ and
-‘object.edit’. This makes a lot of sense for a web application, not so much for a MUD, especially
-when we try to hide away as much of the underlying architecture as possible.
-
-
The django permissions are not completely gone however. We use it for validating passwords during
-login. It is also used exclusively for managing Evennia’s web-based admin site, which is a graphical
-front-end for the database of Evennia. You edit and assign such permissions directly from the web
-interface. It’s stand-alone from the permissions described above.
+
Django also implements a comprehensive permission/security system of its own. The reason we don’t use that is because it is app-centric (app in the Django sense). Its permission strings are of the form appname.permstring and it automatically adds three of them for each database model in the app - for the app evennia/object this would be for example ‘object.create’, ‘object.admin’ and ‘object.edit’. This makes a lot of sense for a web application, not so much for a MUD, especially when we try to hide away as much of the underlying architecture as possible.
+
The django permissions are not completely gone however. We use it for validating passwords during login. It is also used exclusively for managing Evennia’s web-based admin site, which is a graphical front-end for the database of Evennia. You edit and assign such permissions directly from the web interface. It’s stand-alone from the permissions described above.
@@ -508,10 +488,10 @@ interface. It’s stand-alone from the permissions described above.
modules |
The Msg object represents a database-saved
-piece of communication. Think of it as a discrete piece of email - it contains
-a message, some metadata and will always have a sender and one or more
-recipients.
-
Once created, a Msg is normally not changed. It is persitently saved in the
-database. This allows for comprehensive logging of communications. Here are some
-good uses for Msg objects:
+
The Msg object represents a database-saved piece of communication. Think of it as a discrete piece of email - it contains a message, some metadata and will always have a sender and one or more recipients.
+
Once created, a Msg is normally not changed. It is persitently saved in the database. This allows for comprehensive logging of communications. Here are some good uses for Msg objects:
page/tells (the page command is how Evennia uses them out of the box)
messages in a bulletin board
@@ -133,13 +128,10 @@ actual in-game letter-object based on the Msg)
Changed in version 1.0: Channels dropped Msg-support. Now only used in page command by default.
The Msg is intended to be used exclusively in code, to build other game systems. It is not
-a Typeclassed entity, which means it cannot (easily) be overridden. It
-doesn’t support Attributes (but it does support Tags). It tries to be lean
-and small since a new one is created for every message.
-
You create a new message with evennia.create_message:
The Msg is intended to be used exclusively in code, to build other game systems. It is not a Typeclassed entity, which means it cannot (easily) be overridden. It doesn’t support Attributes (but it does support Tags). It tries to be lean and small since a new one is created for every message.
+You create a new message with evennia.create_message:
evennia.comms.models.TempMsg is an object
-that implements the same API as the regular Msg, but which has no database
-component (and thus cannot be searched). It’s meant to plugged into systems
-expecting a Msg but where you just want to process the message without saving
-it.
+
evennia.comms.models.TempMsg is an object that implements the same API as the regular Msg, but which has no database component (and thus cannot be searched). It’s meant to plugged into systems expecting a Msg but where you just want to process the message without saving it.
You can easily add your own in-game behavior by either modifying one of the typeclasses in your game dir or by inheriting from them.
You can put your new typeclass directly in the relevant module, or you could organize your code in some other way. Here we assume we make a new module mygame/typeclasses/flowers.py:
@@ -195,29 +195,9 @@ powerful and should be used for all coded object creating (so this is what you u
your own building commands).
This particular Rose class doesn’t really do much, all it does it make sure the attribute
desc(which is what the look command looks for) is pre-set, which is pretty pointless since you
-will usually want to change this at build time (using the desc command or using the
-Spawner).
Object, Character, Room and Exit also inherit from mygame.typeclasses.objects.ObjectParent.
-This is an empty ‘mixin’ class. Optionally, you can modify this class if you want to easily add some common functionality to all your Objects, Characters, Rooms and Exits at once. You can still customize each subclass separately (see the Python docs on multiple inheritance for details).
-
Here is an example:
-
# in mygame/typeclasses/objects.py
-# ...
-
-fromevennia.objects.objectsimportDefaultObject
-
-classObjectParent:
- defat_pre_get(self,getter,**kwargs):
- # make all entities by default un-pickable
- returnFalse
-
-
-
Now all of Object, Exit. Room and Character default to not being able to be picked up using the get command.
-
+will usually want to change this at build time (using the desc command or using the Spawner).
-
Beyond the properties assigned to all typeclassed objects (see that page for a list
of those), the Object also has the following custom properties:
@@ -250,21 +230,39 @@ of those), the Object also has the following custom properties:
The Object Typeclass defines many more hook methods beyond at_object_creation. Evennia calls these hooks at various points. When implementing your custom objects, you will inherit from the base parent and overload these hooks with your own custom code. See evennia.objects.objects for an updated list of all the available hooks or the API for DefaultObject here.
There are three special subclasses of Object in default Evennia - Characters, Rooms and Exits. The reason they are separated is because these particular object types are fundamental, something you will always need and in some cases requires some extra attention in order to be recognized by the game engine (there is nothing stopping you from redefining them though). In practice they are all pretty similar to the base Object.
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 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 mygame/typeclasses/characters.py is an empty Character class ready for you to modify.
The DefaultCharacters is the root class for player in-game entities. They are usually puppeted 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 (but Evennia supports alternative connection-styles if so desired).
+
A Character object must have a Default Commandset set on itself at creation, or the account will not be able to issue any commands!
+
If you want to change the default character created by the default commands, you can change it in settings:
Rooms are the root containers of all other objects. The only thing really separating a room from any other object is that they have no location of their own and that default commands like @dig creates objects of this class - so if you want to expand your rooms with more functionality, just inherit from ev.DefaultRoom. In mygame/typeclasses/rooms.py is an empty Room class ready for you to modify.
Rooms are the root containers of all other objects.
+
The only thing really separating a room from any other object is that they have no location of their own and that default commands like dig creates objects of this class - so if you want to expand your rooms with more functionality, just inherit from evennia.DefaultRoom.
+
To change the default room created by dig, tunnel and other commands, change it in settings:
+
BASE_ROOM_TYPECLASS = "typeclases.rooms.Room"
+
+
+
The empty class in mygame/typeclasses/rooms.py is a good place to start!
Exits are objects connecting other objects (usually Rooms) together. An object named North or in might be an exit, as well as door, portal or jump out the window. An exit has two things that separate them from other objects. Firstly, their destination property is set and points to a valid object. This fact makes it easy and fast to locate exits in the database. Secondly, exits define a special Transit Command on themselves when they are created. This command is named the same as the exit object and will, when called, handle the practicalities of moving the character to the Exits’s destination - this allows you to just enter the name of the exit on its own to move around, just as you would expect.
-
The exit functionality is all defined on the Exit typeclass, so you could in principle completely change how exits work in your game (it’s not recommended though, unless you really know what you are doing). Exits are locked using an access_type called traverse and also make use of a few hook methods for giving feedback if the traversal fails. See evennia.DefaultExit for more info. In mygame/typeclasses/exits.py there is an empty Exit class for you to modify.
+
The exit functionality is all defined on the Exit typeclass, so you could in principle completely change how exits work in your game (it’s not recommended though, unless you really know what you are doing). Exits are locked using an access_type called traverse and also make use of a few hook methods for giving feedback if the traversal fails. See evennia.DefaultExit for more info.
+
Exits are normally overridden on a case-by-case basis, but if you want to change the default exit createad by rooms like dig , tunnel or open you can change it in settings:
+
BASE_EXIT_TYPECLASS = "typeclasses.exits.Exit"
+
+
+
In mygame/typeclasses/exits.py there is an empty Exit class for you to modify.
The traversing obj sends a command that matches the Exit-command name on the Exit object. The cmdhandler detects this and triggers the command defined on the Exit. Traversal always involves the “source” (the current location) and the destination (this is stored on the Exit object).
@@ -286,6 +284,24 @@ of those), the Object also has the following custom properties:
If the move fails for whatever reason, the Exit will look for an Attribute err_traverse on itself and display this as an error message. If this is not found, the Exit will instead call at_failed_traverse(obj) on itself.
Object, Character, Room and Exit also inherit from mygame.typeclasses.objects.ObjectParent.
+This is an empty ‘mixin’ class. Optionally, you can modify this class if you want to easily add some common functionality to all your Objects, Characters, Rooms and Exits at once. You can still customize each subclass separately (see the Python docs on multiple inheritance for details).
+
Here is an example:
+
# in mygame/typeclasses/objects.py
+# ...
+
+fromevennia.objects.objectsimportDefaultObject
+
+classObjectParent:
+ defat_pre_get(self,getter,**kwargs):
+ # make all entities by default un-pickable
+ returnFalse
+
+
+
Now all of Object, Exit. Room and Character default to not being able to be picked up using the get command.
In-game, you use the perm command to add and remove permissions
> perm/account Tommy = Builders
> perm/account/del Tommy = Builders
@@ -156,9 +155,8 @@ typeclassed entities as the property obj.permissions.remove("Blacksmith")
Selected permission strings can be organized in a permission hierarchy by editing the tuple
settings.PERMISSION_HIERARCHY. Evennia’s default permission hierarchy is as follows
(in increasing order of power):
@@ -176,8 +174,9 @@ typeclassed entities as the property
When checking a hierarchical permission (using one of the methods to follow), you will pass checks for your level and all below you. That is, even if the check explicitly checks for “Builder” level access, you will actually pass if you have one of “Builder”, “Admin” or “Developer”. By contrast, if you check for a non-hierarchical permission, like “Blacksmith” you must have exactly that permission to pass.
It’s important to note that you check for the permission of a puppetedObject (like a Character), the check will always first use the permissions of any Account connected to that Object before checking for permissions on the Object. In the case of hierarchical permissions (Admins, Builders etc), the Account permission will always be used (this stops an Account from escalating their permission by puppeting a high-level Character). If the permission looked for is not in the hierarchy, an exact match is required, first on the Account and if not found there (or if no Account is connected), then on the Object itself.
There is normally only one superuser account and that is the one first created
-when starting Evennia (User #1). This is sometimes known as the “Owner” or “God”
-user. A superuser has more than full access - it completely bypasses all
-locks and will always pass the PermissionHandler.check() check. This allows
-for the superuser to always have access to everything in an emergency. But it
-could also hide any eventual errors you might have made in your lock definitions. So
-when trying out game systems you should either use quelling (see below) or make
-a second Developer-level character that does not bypass such checks.
Evennia consists of two processes, known as Portal and Server. They can be controlled from
-inside the game or from the command line as described in the Running-Evennia doc.
-
In short, the Portal knows everything about internet protocols (telnet, websockets etc), but knows very little about the game.
-
In contrast, the Server knows everything about the game. It knows that a player has connected but now how they connected.
-
The effect of this is that you can fully reload the Server and have players still connected to the game. One the server comes back up, it will re-connect to the Portal and re-sync all players as if nothing happened.
The Portal knows everything about internet protocols (telnet, websockets etc), but knows very little about the game.
+
The Server knows everything about the game. It knows that a player has connected but now how they connected.
+
+
The effect of this is that you can fully reload the Server and have players still connected to the game. One the server comes back up, it will re-connect to the Portal and re-sync all players as if nothing happened.
+
The Portal and Server are intended to always run on the same machine. They are glued together via an AMP (Asynchronous Messaging Protocol) connection. This allows the two programs to communicate seamlessly.
@@ -135,10 +137,10 @@ inside the game or from the command line as described modules |
The spawner is a system for defining and creating individual objects from a base template called a
-prototype. It is only designed for use with in-game Objects, not any other type of
-entity.
-
The normal way to create a custom object in Evennia is to make a Typeclass. If you
-haven’t read up on Typeclasses yet, think of them as normal Python classes that save to the database
-behind the scenes. Say you wanted to create a “Goblin” enemy. A common way to do this would be to
-first create a Mobile typeclass that holds everything common to mobiles in the game, like generic
-AI, combat code and various movement methods. A Goblin subclass is then made to inherit from
-Mobile. The Goblin class adds stuff unique to goblins, like group-based AI (because goblins are
-smarter in a group), the ability to panic, dig for gold etc.
-
But now it’s time to actually start to create some goblins and put them in the world. What if we
-wanted those goblins to not all look the same? Maybe we want grey-skinned and green-skinned goblins
-or some goblins that can cast spells or which wield different weapons? We could make subclasses of
-Goblin, like GreySkinnedGoblin and GoblinWieldingClub. But that seems a bit excessive (and a
-lot of Python code for every little thing). Using classes can also become impractical when wanting
-to combine them - what if we want a grey-skinned goblin shaman wielding a spear - setting up a web
-of classes inheriting each other with multiple inheritance can be tricky.
-
This is what the prototype is for. It is a Python dictionary that describes these per-instance
-changes to an object. The prototype also has the advantage of allowing an in-game builder to
-customize an object without access to the Python backend. Evennia also allows for saving and
-searching prototypes so other builders can find and use (and tweak) them later. Having a library of
-interesting prototypes is a good reasource for builders. The OLC system allows for creating, saving,
-loading and manipulating prototypes using a menu system.
+
> spawn goblin
+
+Spawned Goblin Grunt(#45)
+
+
+
The spawner is a system for defining and creating individual objects from a base template called a prototype. It is only designed for use with in-game Objects, not any other type of entity.
+
The normal way to create a custom object in Evennia is to make a Typeclass. If you haven’t read up on Typeclasses yet, think of them as normal Python classes that save to the database behind the scenes. Say you wanted to create a “Goblin” enemy. A common way to do this would be to first create a Mobile typeclass that holds everything common to mobiles in the game, like generic AI, combat code and various movement methods. A Goblin subclass is then made to inherit from Mobile. The Goblin class adds stuff unique to goblins, like group-based AI (because goblins are smarter in a group), the ability to panic, dig for gold etc.
+
But now it’s time to actually start to create some goblins and put them in the world. What if we wanted those goblins to not all look the same? Maybe we want grey-skinned and green-skinned goblins or some goblins that can cast spells or which wield different weapons? We could make subclasses of Goblin, like GreySkinnedGoblin and GoblinWieldingClub. But that seems a bit excessive (and a lot of Python code for every little thing). Using classes can also become impractical when wanting to combine them - what if we want a grey-skinned goblin shaman wielding a spear - setting up a web of classes inheriting each other with multiple inheritance can be tricky.
+
This is what the prototype is for. It is a Python dictionary that describes these per-instance changes to an object. The prototype also has the advantage of allowing an in-game builder to customize an object without access to the Python backend. Evennia also allows for saving and searching prototypes so other builders can find and use (and tweak) them later. Having a library of interesting prototypes is a good reasource for builders. The OLC system allows for creating, saving, loading and manipulating prototypes using a menu system.
The spawner takes a prototype and uses it to create (spawn) new, custom objects.
Enter the olc command or spawn/olc to enter the prototype wizard. This is a menu system for
creating, loading, saving and manipulating prototypes. It’s intended to be used by in-game builders
and will give a better understanding of prototypes in general. Use help on each node of the menu
for more information. Below are further details about how prototypes work and how they are used.
The prototype dictionary can either be created for you by the OLC (see above), be written manually
-in a Python module (and then referenced by the @spawn command/OLC), or created on-the-fly and
-manually loaded into the spawner function or @spawn command.
-
The dictionary defines all possible database-properties of an Object. It has a fixed set of allowed
-keys. When preparing to store the prototype in the database (or when using the OLC), some
-of these keys are mandatory. When just passing a one-time prototype-dict to the spawner the system
-is
-more lenient and will use defaults for keys not explicitly provided.
The prototype dictionary can either be created for you by the OLC (see above), be written manually in a Python module (and then referenced by the spawn command/OLC), or created on-the-fly and
+manually loaded into the spawner function or spawn command.
+
The dictionary defines all possible database-properties of an Object. It has a fixed set of allowed keys. When preparing to store the prototype in the database (or when using the OLC), some of these keys are mandatory. When just passing a one-time prototype-dict to the spawner the system is more lenient and will use defaults for keys not explicitly provided.
In dictionary form, a prototype can look something like this:
{"prototype_key":"house"
@@ -174,18 +157,13 @@ more lenient and will use defaults for keys not explicitly provided.
If you wanted to load it into the spawner in-game you could just put all on one line:
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](Spawner-and-
-Prototypes#protfuncs), embedded runnable functions that you have full control to check and vet
-before running.
+
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](Spawner-and- Prototypes#protfuncs), embedded runnable functions that you have full control to check and vet before running.
All keys starting with prototype_ are for book keeping.
@@ -200,53 +178,31 @@ left-right inheritance. If this is not given, a prototype_desc - this is optional and used when listing the prototype in in-game listings.
protototype_tags - this is optional and allows for tagging the prototype in order to find it
easier later.
-
prototype_locks - two lock types are supported: edit and spawn. The first lock restricts
-the copying and editing of the prototype when loaded through the OLC. The second determines who
+
prototype_locks - two lock types are supported: edit and spawn. The first lock restricts the copying and editing of the prototype when loaded through the OLC. The second determines who
may use the prototype to create new objects.
The remaining keys determine actual aspects of the objects to spawn from this prototype:
-
key - the main object identifier. Defaults to “Spawned Object X”, where X is a random
-integer.
-
typeclass - A full python-path (from your gamedir) to the typeclass you want to use. If not
-set, the prototype_parent should be
-defined, with typeclass defined somewhere in the parent chain. When creating a one-time
-prototype
-dict just for spawning, one could omit this - settings.BASE_OBJECT_TYPECLASS will be used
-instead.
+
key - the main object identifier. Defaults to “Spawned Object X”, where X is a random integer.
+
typeclass - A full python-path (from your gamedir) to the typeclass you want to use. If not set, the prototype_parent should be defined, with typeclass defined somewhere in the parent chain. When creating a one-time prototype dict just for spawning, one could omit this - settings.BASE_OBJECT_TYPECLASS will be used instead.
location - this should be a #dbref.
-
home - a valid #dbref. Defaults to location or settings.DEFAULT_HOME if location does not
-exist.
+
home - a valid #dbref. Defaults to location or settings.DEFAULT_HOME if location does not exist.
destination - a valid #dbref. Only used by exits.
permissions - list of permission strings, like ["Accounts","may_use_red_door"]
locks - a lock-string like "edit:all();control:perm(Builder)"
aliases - list of strings for use as aliases
tags - list Tags. These are given as tuples (tag,category,data).
attrs - list of Attributes. These are given as tuples (attrname,value,category,lockstring)
-
Any other keywords are interpreted as non-category Attributes and their values.
-This is convenient for simple Attributes - use attrs for full control of Attributes.
+
Any other keywords are interpreted as non-category Attributes and their values. This is convenient for simple Attributes - use attrs for full control of Attributes.
A prototype can inherit by defining a prototype_parent pointing to the name
-(prototype_key of another prototype). If a list of prototype_keys, this
-will be stepped through from left to right, giving priority to the first in
-the list over those appearing later. That is, if your inheritance is
-prototype_parent=('A','B,''C'), and all parents contain colliding keys,
-then the one from A will apply.
-
The prototype keys that start with prototype_* are all unique to each
-prototype. They are never inherited from parent to child.
-
The prototype fields 'attr':[(key,value,category,lockstring),...]
-and 'tags':[(key,category,data),...] are inherited in a complementary
-fashion. That means that only colliding key+category matches will be replaced, not the entire list.
-Remember that the category None is also considered a valid category!
-
Adding an Attribute as a simple key:value will under the hood be translated
-into an Attribute tuple (key,value,None,'') and may replace an Attribute
-in the parent if it the same key and a None category.
-
All other keys (permissions, destination, aliases etc) are completely
-replaced by the child’s value if given. For the parent’s value to be
-retained, the child must not define these keys at all.
+
A prototype can inherit by defining a prototype_parent pointing to the name (prototype_key of another prototype). If a list of prototype_keys, this will be stepped through from left to right, giving priority to the first in the list over those appearing later. That is, if your inheritance is prototype_parent=('A','B,''C'), and all parents contain colliding keys, then the one from A will apply.
+
The prototype keys that start with prototype_* are all unique to each prototype. They are never inherited from parent to child.
+
The prototype fields 'attr':[(key,value,category,lockstring),...] and 'tags':[(key,category,data),...] are inherited in a complementary fashion. That means that only colliding key+category matches will be replaced, not the entire list. Remember that the category None is also considered a valid category!
+
Adding an Attribute as a simple key:value will under the hood be translated into an Attribute tuple (key,value,None,'') and may replace an Attribute in the parent if it the same key and a None category.
+
All other keys (permissions, destination, aliases etc) are completely replaced by the child’s value if given. For the parent’s value to be retained, the child must not define these keys at all.
@@ -258,32 +214,26 @@ retained, the child must not define these keys at all.
-
It can also be a callable. This callable is called without arguments whenever the prototype is
-used to
-spawn a new object:
+
It can also be a callable. This callable is called without arguments whenever the prototype is used to spawn a new object:
{"key":_get_a_random_goblin_name,...}
-
By use of Python lambda one can wrap the callable so as to make immediate settings in the
-prototype:
+
By use of Python lambda one can wrap the callable so as to make immediate settings in the prototype:
{"key":lambda:random.choice(("Urfgar","Rick the smelly","Blargh the foul",...)),...}
Finally, the value can be a prototype function (Protfunc). These look like simple function calls
-that you embed in strings and that has a $ in front, like
+
Finally, the value can be a prototype function (Protfunc). These look like simple function calls that you embed in strings and that has a $ in front, like
{"key":"$choice(Urfgar, Rick the smelly, Blargh the foul)","attrs":{"desc":"This is a large $red(and very red) demon. ""He has $randint(2,5) skulls in a chain around his neck."}
-
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 is a FuncParser function run
-every time the prototype is used to spawn a new object.
-
Here is how a protfunc is defined (same as an inlinefunc).
+
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 is a FuncParser function run every time the prototype is used to spawn a new object.
+
Here is how a protfunc is defined (same as other funcparser functions).
# this is a silly example, you can just color the text red with |r directly!defred(*args,**kwargs):"""
@@ -296,23 +246,17 @@ every time the prototype is used to spawn a new object.
-
Note that we must make sure to validate input and raise ValueError if that fails. Also, it is
-not possible to use keywords in the call to the protfunc (so something like $echo(text,align=left) is invalid). The kwargs requred is for internal evennia use and not used at all for
-protfuncs (only by inlinefuncs).
+
Note that we must make sure to validate input and raise ValueError if that fails. Also, it is not possible to use keywords in the call to the protfunc (so something like $echo(text,align=left) is invalid). The kwargs requred is for internal evennia use and not used at all for protfuncs (only by inlinefuncs).
-
To make this protfunc available to builders in-game, add it to a new module and add the path to that
-module to settings.PROT_FUNC_MODULES:
+
To make this protfunc available to builders in-game, add it to a new module and add the path to that module to settings.PROT_FUNC_MODULES:
# in mygame/server/conf/settings.pyPROT_FUNC_MODULES+=["world.myprotfuncs"]
-
All global callables in your added module will be considered a new protfunc. To avoid this (e.g.
-to have helper functions that are not protfuncs on their own), name your function something starting
-with _.
-
The default protfuncs available out of the box are defined in evennia/prototypes/profuncs.py. To
-override the ones available, just add the same-named function in your own protfunc module.
+
All global callables in your added module will be considered a new protfunc. To avoid this (e.g. to have helper functions that are not protfuncs on their own), name your function something starting with _.
+
The default protfuncs available out of the box are defined in evennia/prototypes/profuncs.py. To override the ones available, just add the same-named function in your own protfunc module.
Protfunc
@@ -370,31 +314,17 @@ override the ones available, just add the same-named function in your own protfu
-
For developers with access to Python, using protfuncs in prototypes is generally not useful. Passing
-real Python functions is a lot more powerful and flexible. Their main use is to allow in-game
-builders to
-do limited coding/scripting for their prototypes without giving them direct access to raw Python.
+
For developers with access to Python, using protfuncs in prototypes is generally not useful. Passing real Python functions is a lot more powerful and flexible. Their main use is to allow in-game builders to do limited coding/scripting for their prototypes without giving them direct access to raw Python.
Stored as Scripts in the database. These are sometimes referred to as database-
-prototypes This is the only way for in-game builders to modify and add prototypes. They have the
-advantage of being easily modifiable and sharable between builders but you need to work with them
-using in-game tools.
Stored as Scripts in the database. These are sometimes referred to as database- prototypes This is the only way for in-game builders to modify and add prototypes. They have the advantage of being easily modifiable and sharable between builders but you need to work with them using in-game tools.
These prototypes are defined as dictionaries assigned to global variables in one of the modules
-defined in settings.PROTOTYPE_MODULES. They can only be modified from outside the game so they are
-are necessarily “read-only” from in-game and cannot be modified (but copies of them could be made
-into database-prototypes). These were the only prototypes available before Evennia 0.8. Module based
-prototypes can be useful in order for developers to provide read-only “starting” or “base”
-prototypes to build from or if they just prefer to work offline in an external code editor.
These prototypes are defined as dictionaries assigned to global variables in one of the modules defined in settings.PROTOTYPE_MODULES. They can only be modified from outside the game so they are are necessarily “read-only” from in-game and cannot be modified (but copies of them could be made into database-prototypes). These were the only prototypes available before Evennia 0.8. Module based prototypes can be useful in order for developers to provide read-only “starting” or “base” prototypes to build from or if they just prefer to work offline in an external code editor.
By default mygame/world/prototypes.py is set up for you to add your own prototypes. All global
dicts in this module will be considered by Evennia to be a prototype. You could also tell Evennia
to look for prototypes in more modules if you want:
@@ -423,17 +353,14 @@ was given explicitly, that would take precedence. This is a legacy behavior and
that you always add prototype_key to be consistent.
The spawner can be used from inside the game through the Builder-only @spawn command. Assuming the
-“goblin” typeclass is available to the system (either as a database-prototype or read from module),
-you can spawn a new goblin with
The spawner can be used from inside the game through the Builder-only @spawn command. Assuming the “goblin” typeclass is available to the system (either as a database-prototype or read from module), you can spawn a new goblin with
+
spawn goblin
You can also specify the prototype directly as a valid Python dictionary:
-
@spawn {"prototype_key": "shaman", \
+
spawn {"prototype_key": "shaman", \
"key":"Orc shaman", \
"prototype_parent": "goblin", \
"weapon": "wooden staff", \
@@ -441,14 +368,10 @@ you can spawn a new goblin with
-
Note: The @spawn command is more lenient about the prototype dictionary than shown here. So you
-can for example skip the prototype_key if you are just testing a throw-away prototype. A random
-hash will be used to please the validation. You could also skip prototype_parent/typeclass - then
-the typeclass given by settings.BASE_OBJECT_TYPECLASS will be used.
+
Note: The @spawncommandismorelenientabouttheprototypedictionarythanshownhere.Soyoucanforexampleskiptheprototype_keyifyouarejusttestingathrow-awayprototype.Arandomhashwillbeusedtopleasethevalidation.Youcouldalsoskipprototype_parent/typeclass-thenthetypeclassgivenbysettings.BASE_OBJECT_TYPECLASS` will be used.
@@ -460,18 +383,10 @@ matching list of created objects. Example:
-
Hint: Same as when using @spawn, when spawning from a one-time prototype dict like this, you can
-skip otherwise required keys, like prototype_key or typeclass/prototype_parent. Defaults will
-be used.
+
Hint: Same as when using spawn, when spawning from a one-time prototype dict like this, you can skip otherwise required keys, like prototype_key or typeclass/prototype_parent. Defaults will be used.
-
Note that no location will be set automatically when using evennia.prototypes.spawner.spawn(),
-you
-have to specify location explicitly in the prototype dict.
-
If the prototypes you supply are using prototype_parent keywords, the spawner will read prototypes
-from modules
-in settings.PROTOTYPE_MODULES as well as those saved to the database to determine the body of
-available parents. The spawn command takes many optional keywords, you can find its definition in
-the api docs.
+
Note that no location will be set automatically when using evennia.prototypes.spawner.spawn(), you have to specify location explicitly in the prototype dict. If the prototypes you supply are using prototype_parent keywords, the spawner will read prototypes from modules in settings.PROTOTYPE_MODULES as well as those saved to the database to determine the body of available parents. The spawn command takes many optional keywords, you can find its definition in the api docs.
Scripts are the out-of-character siblings to the in-character
-Objects. Scripts are so flexible that the name “Script” is a bit limiting
-in itself - but we had to pick something to name them. Other possible names
-(depending on what you’d use them for) would be OOBObjects, StorageContainers or TimerObjects.
-
If you ever consider creating an Object with a None-location just to store some game data,
-you should really be using a Script instead.
+
Scripts are the out-of-character siblings to the in-character Objects. Scripts are so flexible that the name “Script” is a bit limiting in itself - but we had to pick something to name them. Other possible names (depending on what you’d use them for) would be OOBObjects, StorageContainers or TimerObjects.
+
If you ever consider creating an Object with a None-location just to store some game data, you should really be using a Script instead.
-
Scripts are full Typeclassed entities - they have Attributes and
-can be modified in the same way. But they have no in-game existence, so no
-location or command-execution like Objects and no connection to a particular
-player/session like Accounts. This means they are perfectly suitable for acting
-as database-storage backends for game systems: Storing the current state of the economy,
-who is involved in the current fight, tracking an ongoing barter and so on. They are great as
-persistent system handlers.
-
Scripts have an optional timer component. This means that you can set up the script
-to tick the at_repeat hook on the Script at a certain interval. The timer can be controlled
-independently of the rest of the script as needed. This component is optional
-and complementary to other timing functions in Evennia, like
-evennia.utils.delay and
-evennia.utils.repeat.
-
Scripts can attach to Objects and Accounts via e.g. obj.scripts.add/remove. In the
-script you can then access the object/account as self.obj or self.account. This can be used to
-dynamically extend other typeclasses but also to use the timer component to affect the parent object
-in various ways. For historical reasons, a Script not attached to an object is referred to as a
-Global Script.
+
Scripts are full Typeclassed entities - they have Attributes and can be modified in the same way. But they have no in-game existence, so no location or command-execution like Objects and no connection to a particular player/session like Accounts. This means they are perfectly suitable for acting as database-storage backends for game systems: Storing the current state of the economy, who is involved in the current fight, tracking an ongoing barter and so on. They are great as persistent system handlers.
+
Scripts have an optional timer component. This means that you can set up the script to tick the at_repeat hook on the Script at a certain interval. The timer can be controlled independently of the rest of the script as needed. This component is optional and complementary to other timing functions in Evennia, like evennia.utils.delay and evennia.utils.repeat.
+
Scripts can attach to Objects and Accounts via e.g. obj.scripts.add/remove. In the script you can then access the object/account as self.obj or self.account. This can be used to dynamically extend other typeclasses but also to use the timer component to affect the parent object in various ways. For historical reasons, a Script not attached to an object is referred to as a Global Script.
Changed in version 1.0: In previus Evennia versions, stopping the Script’s timer also meant deleting the Script object.
Starting with this version, the timer can be start/stopped separately and .delete() must be called
on the Script explicitly to delete it.
There are several properties one can set on the Script to control its timer component.
# in mygame/typeclasses/scripts.py
@@ -293,8 +270,7 @@ to help identifying what does what.
interval (int): The amount of time (in seconds) between every ‘tick’ of the timer. Note that
it’s generally bad practice to use sub-second timers for anything in a text-game - the player will
not be able to appreciate the precision (and if you print it, it will just spam the screen). For
-calculations you can pretty much always do them on-demand, or at a much slower interval without the
-player being the wiser.
+calculations you can pretty much always do them on-demand, or at a much slower interval without the player being the wiser.
start_delay (bool): If timer should start right away or wait interval seconds first.
repeats (int): If >0, the timer will only run this many times before stopping. Otherwise the
number of repeats are infinite. If set to 1, the Script mimics a delay action.
@@ -320,30 +296,18 @@ at which time the m
.time_until_next_repeat() - get the time until next time the timer fires.
.remaining_repeats() - get the number of repeats remaining, or None if repeats are infinite.
.reset_callcount() - this resets the repeat counter to start over from 0. Only useful if repeats>0.
-
.force_repeat() - this prematurely forces at_repeat to be called right away. Doing so will reset the
-countdown so that next call will again happen after interval seconds.
+
.force_repeat() - this prematurely forces at_repeat to be called right away. Doing so will reset the countdown so that next call will again happen after interval seconds.
If the only goal is to get a repeat/delay effect, the
-evennia.utils.delay and
-evennia.utils.repeat functions
-should generally be considered first. A Script is a lot ‘heavier’ to create/delete on the fly.
-In fact, for making a single delayed call (script.repeats==1), the utils.delay call is
-probably always the better choice.
-
For repeating tasks, the utils.repeat is optimized for quick repeating of a large number of objects. It
-uses the TickerHandler under the hood. Its subscription-based model makes it very efficient to
-start/stop the repeating action for an object. The side effect is however that all objects set to tick
-at a given interval will all do so at the same time. This may or may not look strange in-game depending
-on the situation. By contrast the Script uses its own ticker that will operate independently from the
-tickers of all other Scripts.
-
It’s also worth noting that once the script object has already been created,
-starting/stopping/pausing/unpausing the timer has very little overhead. The pause/unpause and update
-methods of the script also offers a bit more fine-control than using utils.delays/repeat.
-
+
If the only goal is to get a repeat/delay effect, the evennia.utils.delay and evennia.utils.repeat functions should generally be considered first. A Script is a lot ‘heavier’ to create/delete on the fly. In fact, for making a single delayed call (script.repeats==1), the utils.delay call is probably always the better choice.
+
For repeating tasks, the utils.repeat is optimized for quick repeating of a large number of objects. It uses the TickerHandler under the hood. Its subscription-based model makes it very efficient to start/stop the repeating action for an object. The side effect is however that all objects set to tick at a given interval will all do so at the same time. This may or may not look strange in-game depending on the situation. By contrast the Script uses its own ticker that will operate independently from the tickers of all other Scripts.
+
It’s also worth noting that once the script object has already been created, starting/stopping/pausing/unpausing the timer has very little overhead. The pause/unpause and update methods of the script also offers a bit more fine-control than using utils.delays/repeat.
Scripts can be attached to an Account or (more commonly) an Object.
If so, the ‘parent object’ will be available to the script as either .obj or .account.
# mygame/typeclasses/scripts.py
@@ -392,20 +356,16 @@ but only scripts.We
A Script has all the properties of a typeclassed object, such as db and ndb(see
Typeclasses). Setting key is useful in order to manage scripts (delete them by name
etc). These are usually set up in the Script’s typeclass, but can also be assigned on the fly as
keyword arguments to evennia.create_script.
at_script_creation() - this is only called once - when the script is first created.
-
at_server_reload() - this is called whenever the server is warm-rebooted (e.g. with the
-reload command). It’s a good place to save non-persistent data you might want to survive a
-reload.
+
at_server_reload() - this is called whenever the server is warm-rebooted (e.g. with the reload command). It’s a good place to save non-persistent data you might want to survive a reload.
at_server_shutdown() - this is called when a system reset or systems shutdown is invoked.
-
at_server_start() - this is called when the server comes back (from reload/shutdown/reboot). It
-can be usuful for initializations and caching of non-persistent data when starting up a script’s
-functionality.
+
at_server_start() - this is called when the server comes back (from reload/shutdown/reboot). It can be usuful for initializations and caching of non-persistent data when starting up a script’s functionality.
at_repeat()
at_start()
at_pause()
@@ -413,12 +373,33 @@ functionality.
delete() - same as for other typeclassed entities, this will delete the Script. Of note is that
it will also stop the timer (if it runs), leading to the at_stop hook being called.
-
In addition, Scripts support Attributes, Tags and Locks etc like other
-Typeclassed entities.
-
See also the methods involved in controlling a Timed Script above.
+
In addition, Scripts support Attributes, Tags and Locks etc like other Typeclassed entities.
+
See also the methods involved in controlling a Timed Script above.
Errors inside a timed, executing script can sometimes be rather terse or point to parts of the execution mechanism that is hard to interpret. One way to make it easier to debug scripts is to import Evennia’s native logger and wrap your functions in a try/catch block. Evennia’s logger can show you where the traceback occurred in your script.
A Script not attached to another entity is commonly referred to as a Global script since it’t available
to access from anywhere. This means they need to be searched for in order to be used.
Evennia supplies a convenient “container” evennia.GLOBAL_SCRIPTS to help organize your global
@@ -438,13 +419,7 @@ scripts. All you need is the Script’s
Warning
-
Note that global scripts appear as properties on `GLOBAL_SCRIPTS` based on their `key`.
-If you were to create two global scripts with the same `key` (even with different typeclasses),
-the `GLOBAL_SCRIPTS` container will only return one of them (which one depends on order in
-the database). Best is to organize your scripts so that this does not happen. Otherwise, use
-`evennia.search_scripts` to get exactly the script you want.
-
-
+
Note that global scripts appear as properties on GLOBAL_SCRIPTS based on their key. If you were to create two global scripts with the same key (even with different typeclasses), the GLOBAL_SCRIPTS container will only return one of them (which one depends on order in the database). Best is to organize your scripts so that this does not happen. Otherwise, use evennia.search_scripts to get exactly the script you want.
There are two ways to make a script appear as a property on GLOBAL_SCRIPTS:
@@ -469,18 +444,10 @@ script keys are globally unique.
}
-
Above we add two scripts with keys myscript and storagescriptrespectively. The following dict
-can be empty - the settings.BASE_SCRIPT_TYPECLASS will then be used. Under the hood, the provided
-dict (along with the key) will be passed into create_script automatically, so
-all the same keyword arguments as for create_script are
-supported here.
+
Above we add two scripts with keys myscript and storagescriptrespectively. The following dict can be empty - the settings.BASE_SCRIPT_TYPECLASS will then be used. Under the hood, the provided dict (along with the key) will be passed into create_script automatically, so all the same keyword arguments as for create_script are supported here.
Warning
-
Before setting up Evennia to manage your script like this, make sure that your Script typeclass
-does not have any critical errors (test it separately). If there are, you'll see errors in your log
-and your Script will temporarily fall back to being a `DefaultScript` type.
-
-
+
Before setting up Evennia to manage your script like this, make sure that your Script typeclass does not have any critical errors (test it separately). If there are, you’ll see errors in your log and your Script will temporarily fall back to being a DefaultScript type.
Moreover, a script defined this way is guaranteed to exist when you try to access it:
fromevenniaimportGLOBAL_SCRIPTS
@@ -494,29 +461,6 @@ and your Script will temporarily fall back to being a `DefaultScript` type.
That is, if the script is deleted, next time you get it from GLOBAL_SCRIPTS, Evennia will use the
information in settings to recreate it for you on the fly.
Errors inside a timed, executing script can sometimes be rather terse or point to
-parts of the execution mechanism that is hard to interpret. One way to make it
-easier to debug scripts is to import Evennia’s native logger and wrap your
-functions in a try/catch block. Evennia’s logger can show you where the
-traceback occurred in your script.
@@ -122,17 +126,17 @@ Evennia session, it is possible for a person to connect multiple times, for exam
clients in multiple windows. Each such connection is represented by a session object.
A session object has its own cmdset, usually the “unloggedin” cmdset. This is what is used to show the login screen and to handle commands to create a new account (or Account in evennia lingo) read initial help and to log into the game with an existing account. A session object can either be “logged in” or not. Logged in means that the user has authenticated. When this happens the session is associated with an Account object (which is what holds account-centric stuff). The account can then in turn puppet any number of objects/characters.
A Session is not persistent - it is not a Typeclass and has no connection to the database. The Session will go away when a user disconnects and you will lose any custom data on it if the server reloads. The .db handler on Sessions is there to present a uniform API (so you can assume .db exists even if you don’t know if you receive an Object or a Session), but this is just an alias to .ndb. So don’t store any data on Sessions that you can’t afford to lose in a reload.
The number of sessions possible to connect to a given account at the same time and how it works is given by the MULTISESSION_MODE setting:
-
-
MULTISESSION_MODE=0: One session per account. When connecting with a new session the old one is disconnected. This is the default mode and emulates many classic mud code bases.
MULTISESSION_MODE=1: Many sessions per account, input/output from/to each session is treated the same. For the player this means they can connect to the game from multiple clients and see the same output in all of them. The result of a command given in one client (that is, through one Session) will be returned to all connected Sessions/clients with no distinction.
MULTISESSION_MODE=2: Many sessions per account, one character per session. In this mode, puppeting an Object/Character will link the puppet back only to the particular Session doing the puppeting. That is, input from that Session will make use of the CmdSet of that Object/Character and outgoing messages (such as the result of a look) will be passed back only to that puppeting Session. If another Session tries to puppet the same Character, the old Session will automatically un-puppet it. From the player’s perspective, this will mean that they can open separate game clients and play a different Character in each using one game account.
MULTISESSION_MODE=3: Many sessions per account and character. This is the full multi-puppeting mode, where multiple sessions may not only connect to the player account but multiple sessions may also puppet a single character at the same time. From the user’s perspective it means one can open multiple client windows, some for controlling different Characters and some that share a Character’s input/output like in mode 1. This mode otherwise works the same as mode 2.
When you use msg() to return data to a user, the object on which you call the msg() matters. The
MULTISESSION_MODE also matters, especially if greater than 1.
For example, if you use account.msg("hello") there is no way for evennia to know which session it
@@ -231,44 +176,23 @@ script triggers the Command.
When would one want to customize the Session object? Consider for example a character creation
-system: You might decide to keep this on the out-of-character level. This would mean that you create
-the character at the end of some sort of menu choice. The actual char-create cmdset would then
-normally be put on the account. This works fine as long as you are MULTISESSION_MODE below 2.
-For higher modes, replacing the Account cmdset will affect all your connected sessions, also those
-not involved in character creation. In this case you want to instead put the char-create cmdset on
-the Session level - then all other sessions will keep working normally despite you creating a new
-character in one of them.
-
By default, the session object gets the commands.default_cmdsets.UnloggedinCmdSet when the user
-first connects. Once the session is authenticated it has no default sets. To add a “logged-in”
-cmdset to the Session, give the path to the cmdset class with settings.CMDSET_SESSION. This set
+
When would one want to customize the Session object? Consider for example a character creation system: You might decide to keep this on the out-of-character level. This would mean that you create the character at the end of some sort of menu choice. The actual char-create cmdset would then normally be put on the account. This works fine as long as you are MULTISESSION_MODE below 2. For higher modes, replacing the Account cmdset will affect all your connected sessions, also those not involved in character creation. In this case you want to instead put the char-create cmdset on the Session level - then all other sessions will keep working normally despite you creating a new character in one of them.
+
By default, the session object gets the commands.default_cmdsets.UnloggedinCmdSet when the user first connects. Once the session is authenticated it has no default sets. To add a “logged-in” cmdset to the Session, give the path to the cmdset class with settings.CMDSET_SESSION. This set
will then henceforth always be present as soon as the account logs in.
-
To customize further you can completely override the Session with your own subclass. To replace the
-default Session class, change settings.SERVER_SESSION_CLASS to point to your custom class. This is
-a dangerous practice and errors can easily make your game unplayable. Make sure to take heed of the
-original and make your
-changes carefully.
+
To customize further you can completely override the Session with your own subclass. To replace the default Session class, change settings.SERVER_SESSION_CLASS to point to your custom class. This is a dangerous practice and errors can easily make your game unplayable. Make sure to take heed of the original and make your changes carefully.
Note: This is considered an advanced topic. You don’t need to know this on a first read-through.
-
Evennia is split into two parts, the Portal and the Server. Each side tracks
-its own Sessions, syncing them to each other.
+
Evennia is split into two parts, the Portal and the Server. Each side tracks its own Sessions, syncing them to each other.
The “Session” we normally refer to is actually the ServerSession. Its counter-part on the Portal
side is the PortalSession. Whereas the server sessions deal with game states, the portal session
deals with details of the connection-protocol itself. The two are also acting as backups of critical
data such as when the server reboots.
-
New Account connections are listened for and handled by the Portal using the [protocols](Portal-And-
-Server) it understands (such as telnet, ssh, webclient etc). When a new connection is established, a
-PortalSession is created on the Portal side. This session object looks different depending on
-which protocol is used to connect, but all still have a minimum set of attributes that are generic
-to all
-sessions.
-
These common properties are piped from the Portal, through the AMP connection, to the Server, which
-is now informed a new connection has been established. On the Server side, a ServerSession object
-is created to represent this. There is only one type of ServerSession; It looks the same
-regardless of how the Account connects.
+
New Account connections are listened for and handled by the Portal using the [protocols](Portal-And- Server) it understands (such as telnet, ssh, webclient etc). When a new connection is established, a PortalSession is created on the Portal side. This session object looks different depending on which protocol is used to connect, but all still have a minimum set of attributes that are generic to all sessions.
+
These common properties are piped from the Portal, through the AMP connection, to the Server, which is now informed a new connection has been established. On the Server side, a ServerSession object is created to represent this. There is only one type of ServerSession; It looks the same regardless of how the Account connects.
From now on, there is a one-to-one match between the ServerSession on one side of the AMP
connection and the PortalSession on the other. Data arriving to the Portal Session is sent on to
its mirror Server session and vice versa.
@@ -284,26 +208,21 @@ Portal side. When the Server comes back up, this data is returned by the Portal
in sync. This way an Account’s login status and other connection-critical things can survive a
server reboot (assuming the Portal is not stopped at the same time, obviously).
-
-
Both the Portal and Server each have a sessionhandler to manage the connections. These handlers
are global entities contain all methods for relaying data across the AMP bridge. All types of
Sessions hold a reference to their respective Sessionhandler (the property is called
-sessionhandler) so they can relay data. See protocols for more info
-on building new protocols.
-
To get all Sessions in the game (i.e. all currently connected clients), you access the server-side
-Session handler, which you get by
+sessionhandler) so they can relay data. See protocols for more info on building new protocols.
+
To get all Sessions in the game (i.e. all currently connected clients), you access the server-side Session handler, which you get by
@@ -119,8 +121,8 @@ you can “attach” any number of event-handlers to these signals. You can atta
any number of handlers and they’ll all fire whenever any entity triggers the
signal.
All signals (including some django-specific defaults) are available in the module
evennia.server.signals
(with a shortcut evennia.signals). Signals are named by the sender type. So SIGNAL_ACCOUNT_*
@@ -216,6 +217,7 @@ decorator (only relevant for unit testing)
connection_creation - sent when making initial connection to database.
+
@@ -237,7 +239,7 @@ decorator (only relevant for unit testing)
next |
Above, the tags inform us that the Sword is both sharp and can be wielded. If that’s all they do, they could just be a normal Python flag. When tags become important is if there are a lot of objects with different combinations of tags. Maybe you have a magical spell that dulls all sharp-edged objects in the castle - whether sword, dagger, spear or kitchen knife! You can then just grab all objects with the has_sharp_edge tag.
Another example would be a weather script affecting all rooms tagged as outdoors or finding all characters tagged with belongs_to_fighter_guild.
In Evennia, Tags are technically also used to implement Aliases (alternative names for objects) and Permissions (simple strings for Locks to check for).
Tags are unique. This means that there is only ever one Tag object with a given key and category.
Not specifying a category (default) gives the tag a category of None, which is also considered a
@@ -172,10 +176,8 @@ free up the category property for any use you desire.
You can tag any typeclassed object, namely Objects, Accounts,
-Scripts and Channels. General tags are added by the Taghandler. The
-tag handler is accessed as a property tags on the relevant entity:
You can tag any typeclassed object, namely Objects, Accounts, Scripts and Channels. General tags are added by the Taghandler. The tag handler is accessed as a property tags on the relevant entity:
mychair.tags.add("furniture")mychair.tags.add("furniture",category="luxurious")myroom.tags.add("dungeon#01")
@@ -198,7 +200,7 @@ You can also use the default This tags the chair with a ‘furniture’ Tag (the one with a None category).
-
Usually tags are used as a quick way to find tagged database entities. You can retrieve all objects
with a given Tag like this in code:
importevennia
@@ -222,14 +224,9 @@ with a given Tag like this in code:
-
Note that searching for just “furniture” will only return the objects tagged with the “furniture”
-tag that
-has a category of None. We must explicitly give the category to get the “luxurious” furniture.
+
Note that searching for just “furniture” will only return the objects tagged with the “furniture” tag that has a category of None. We must explicitly give the category to get the “luxurious” furniture.
-
Using any of the search_tag variants will all return Django
-Querysets, including if you only have
-one match. You can treat querysets as lists and iterate over them, or continue building search
-queries with them.
+
Using any of the search_tag variants will all return Django Querysets, including if you only have one match. You can treat querysets as lists and iterate over them, or continue building search queries with them.
Remember when searching that not setting a category means setting it to None - this does not
mean that category is undefined, rather None is considered the default, unnamed category.
importevennia
@@ -244,16 +241,16 @@ mean that category is undefined, rather objs =evennia.search_tag("foo",category="bar")# orobjs=evennia.search_tag(category="bar")
-
There is also an in-game command that deals with assigning and using (Object-) tags:
Aliases and Permissions are implemented using normal TagHandlers that simply save Tags with a
different tagtype. These handlers are named aliases and permissions on all Objects. They are
used in the same way as Tags above:
@@ -267,17 +264,6 @@ used in the same way as Tags above:
and so on. Similarly to how @tag works in-game, there is also the @perm command for assigning
permissions and @alias command for aliases.
Generally, tags are enough on their own for grouping objects. Having no tag category is perfectly
-fine and the normal operation. Simply adding a new Tag for grouping objects is often better than
-making a new category. So think hard before deciding you really need to categorize your Tags.
-
That said, tag categories can be useful if you build some game system that uses tags. You can then
-use tag categories to make sure to separate tags created with this system from any other tags
-created elsewhere. You can then supply custom search methods that only find objects tagged with
-tags of that category. An example of this
-is found in the Zone tutorial.