Updated and cleaned the wiki2rest converter. The ReST documentation should look a lot better now, with less weirdness. Using a python google-code snippet to convert now, so no more need for third-party ruby downloads! This should transfer to readthedocs shortly.

This commit is contained in:
Griatch 2012-06-26 17:45:12 +02:00
parent 43f16094c1
commit ae0f7a04c5
55 changed files with 3990 additions and 1778 deletions

View file

@ -1,3 +1,5 @@
Details on how to use and extend the command system.
Command system
==============
@ -17,21 +19,24 @@ should look to them for inspiration and inherit your own designs from
them.
There are two components to having a command running - the *Command*
class and the *Command Set* you want to store that command in.
class and the *Command Set*.
A *Command* is a python class containing all the functioning code for
what a command does - for example, a *look* command would contain code
for describing other objects.
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) is a python class
holding any number of Command class instances, and one Command can go
into many different command sets. By storing 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*.
A *Command Set* (often referred to as a CmdSet) is like a container for
one or more Commands. A given Command can go into any number of
different command sets. 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. There is also
a step-by-step `beginner's tutorial <AddingCommandTutorial.html>`_ that
will get you started quickly without the explanations.
Defining a Command
------------------
@ -53,56 +58,59 @@ class directly.
This is the help-text for the command
"""
key = "mycommand"
locks = "cmd:all()" # this lock means cmd is available to all
def parse(self):
# parsing the command line here
def func(self):
# executing the command here
# executing the command here
You define a new command by assigning a few class-global properties on
your inherited class and overloading one or two hook functions. The full
gritty mechanic behind how commands work are found towards the end of
this page; for now you only need to know that the command handler
creates an instance of this class when you call that command, then
assigns the new object a few useful properties that you can assume to
always be available.
creates an instance of this class and uses that instance whenever you
use this command - it also dynamically assigns the new command instance
a few useful properties that you can assume to always be available.
Properties assigned to the command instance at run-time
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Let's say player *Bob* with a character *!BigGuy* enters the command
``look at sword``. After the system having successfully identified this
a the "look" command and determined that *!BigGuy* really has access to
a command named ``look``, it chugs the ``look`` command class out of
storage and creates a new instance of it. After some more checks it then
assigns it the following properties:
Let's say player *Bob* with a character *BigGuy* enters the command
*look at sword*. After the system having successfully identified this a
the "look" command and determined that *BigGuy* really has access to a
command named ``look``, it chugs the ``look`` command class out of
storage and either loads an existing Command instance from cache or
creates one. After some more checks it then assigns it the following
properties:
- ``caller`` - a reference to the object executing the command - this
is normally *!BigGuy*, not *Bob*! The command system usually deals
with things on the character level. If you want to do something to
the player in your command, do so through ``caller.player``. The only
exception to this is if you stored your cmdset directly on the
*Player* object (usually used only when a Player has no character
assigned to them, like when creating a new one).
- ``cmdstring`` - the matched key for the command. This would be
"``look``\ " in our example.
is normally the Character\_BigGuy\_, not his Player *Bob* ! If you
want to do something to the player (*Bob*) in your command, do so
through ``caller.player``. (Since cmdsets can be put directly on
Players, caller *can* be a Player object as well, such commands are
usually quite specific though).
- ``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 simply
be "``at sword``\ ".
So if the string entered was *look at sword*, ``args`` would be "*at
sword*\ ".
- ``obj`` - the game `Object <Objects.html>`_ 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 any
interactive object with commands defined on it, like in the example
of the "check time" command defined on a "Clock" object or a `red
button <https://code.google.com/p/evennia/source/browse/trunk/game/gamesrc/objects/examples/red<i>button.py.html>`_
(default) command, this is probably defined directly on *BigGuy* - so
``obj`` will point to BigGuy. Otherwise ``obj`` could be a Player or
any interactive object with commands defined on it, like in the
example of the "check time" command defined on a "Clock" object or a
`red
button <https://code.google.com/p/evennia/source/browse/trunk/game/gamesrc/objects/examples/red_button.py>`_
that you can "``push``\ ".
- ``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 <HelpSystem.html>`_
(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\_).
(*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.
Defining your own command classes
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@ -111,43 +119,44 @@ Beyond the properties Evennia always assigns to the command at runtime
(listed above), your job is to define the following class properties:
- ``key`` (string) - the identifier for the command, like ``look``.
This should (ideally) be unique. it can be more than one word long in
a string, like "press button" or "pull lever left".
This should (ideally) be unique. A key can consist of more than one
word, like "press button" or "pull left lever".
- ``aliases`` (optional list) - a list of alternate names for the
command (``["l", "glance", "see"]``). Same name rules as for ``key``
applies.
- ``locks`` (string) - a `lock definition <Locks.html>`_, usually on
the form ``cmd:<lockfuncs>``. Locks is a rather big topic, so until
you learn more about locks, stick to giving the lockstring
``"cmd:all()"`` to make the command available to everyone.
``"cmd:all()"`` to make the command available to everyone (if you
don't put a lock string, this will be assigned for you).
- ``help_category`` (optional string) - setting this helps to structure
the auto-help into categories. If none is set, this will be set to
*General*.
- ``save_for_next`` (optional boolean). This defaults to ``False``. If
``True``, 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 called by retrieving ``self.caller.ndb.last_cmd``. The next
``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): This should be given as a `raw
- 'arg\_regex' (optional raw string): This should be given as a `raw
regular expression string <http://docs.python.org/library/re.html>`_.
This will be compiled by the system at runtime. This allows you to
customize how the part*\ immediately following the command name (or
alias) must look in order for the parser to match for this command.
Normally the parser is highly efficient in picking out the command
name, also as the beginning of a longer word (as long as the longer
word is not a command name in it self). So ``"lookme"`` will be
parsed as the command ``"look"`` followed by the argument ``"me"``.
By using ``arg_regex`` you could for example force the parser to
require an optional space following the command name (regex string
for this would be ``r"\s.*?|$"``). In that case, ``"lookme"`` will
lead to an "command not found" error while ``"look me"`` will work as
expected.
- autohelp (optional boolean). Defaults to ``True``. This allows for
turning off the `auto-help
system <HelpSystem#Command<i>Auto-help</i>system.html>`_ 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.
The regex will be compiled by the system at runtime. This allows you
to customize how the part *immediately following* the command name
(or alias) must look in order for the parser to match for this
command. Normally the parser is highly efficient in picking out the
command name, also as the beginning of a longer word (as long as the
longer word is not a command name in it self). So ``"lookme"`` will
be parsed as the command ``"look"`` followed by the argument
``"me"``. By using ``arg_regex`` you could for example force the
parser to require a space to follow the command name (regex string
for this would be ``r"\s.*?|$"``). In that case, only ``"look me"``
will work whereas ``"lookme"`` will lead to an "command not found"
error.
- auto\_help (optional boolean). Defaults to ``True``. This allows for
turning off the
[`HelpSystem <HelpSystem.html>`_\ #Command\_Auto-help\_system
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.
You should also implement at least two methods, ``parse()`` and
``func()`` (You could also implement ``perm()``, but that's not needed
@ -155,9 +164,14 @@ unless you want to fundamentally change how access checks work).
``parse()`` is intended to parse the arguments (``self.args``) of the
function. You can do this in any way you like, then store the result(s)
in variable(s) on the command object itself (i.e. on ``self``). The
default mux-like system uses this method to detect "command switches",
to take one example.
in variable(s) on the command object itself (i.e. on ``self``). To take
an example, the default mux-like system uses this method to detect
"command switches" and store them as a list in ``self.switches``. Since
the parsing is usually quite similar inside a command scheme you should
make ``parse()`` as generic as possible and then inherit from it rather
than re-implementing it over and over. In this way, the default
``MuxCommand`` class implements a ``parse()`` for all child commands to
use.
``func()`` is called right after ``parse()`` and should make use of the
pre-parsed input to actually do whatever the command is supposed to do.
@ -170,11 +184,55 @@ by the `Help system <HelpSystem.html>`_ to create the help entry for
this command. You should decide on a way to format your help and stick
to that.
Below is how you define a simple alternative "``look at``\ " command:
Below is how you define a simple alternative "``smile``\ " command:
::
from ev import Commandclass CmdLookAt(Command): """ An alternative (and silly) look command Usage: look at <what> Where <what> may only be 'here' in this example. This initial string (the __doc__ string) is also used to auto-generate the help for this command ... """ key = "look at" # this is the command name to use aliases = ["la", "look a"] # aliases to the command name locks = "cmd:all()" help_category = "General" def parse(self): "Very trivial parser" self.what = self.args.strip() def func(self): "This actually does things" caller = self.caller if not self.what: caller.msg("Look at what?") elif self.what == 'here': # look at the current location description = caller.location.db.desc caller.msg(description) else: # we don't add any more functionality in this example caller.msg("Sorry, you can only look 'here'...")
from ev import Command
class CmdSmile(Command):
"""
A smile command
Usage:
smile [at] [<someone>]
grin [at] [<someone>]
Smiles to someone in your vicinity or to the room
in general.
(This initial string (the __doc__ string)
is also used to auto-generate the help
for this command)
"""
key = "smile"
aliases = ["smile at", "grin", "grin at"]
locks = "cmd:all()"
help_category = "General"
def parse(self):
"Very trivial parser"
self.target = self.args.strip()
def func(self):
"This actually does things"
caller = self.caller
if not self.target or self.target == "here":
string = "%s smiles." % caller.name
caller.location.msg_contents(string, exclude=caller)
caller.msg("You smile.")
else:
target = self.search(self.target)
if not target:
# self.search handles error messages
return
string = "%s smiles to you." % caller.name
target.msg(string)
string = "You smile to %s." % target.name
caller.msg(string)
string = "%s smiles to %s." % (caller.name, target.name)
caller.location.msg_contents(string, exclude=[caller,target])
The power of having commands as classes and to separate ``parse()`` and
``func()`` lies in the ability to inherit functionality without having
@ -185,27 +243,25 @@ specifics of MUX-like commands. Almost none of the default commands thus
need to implement ``parse()`` at all, but can assume the incoming string
is already split up and parsed in suitable ways by its parent.
So we have created our own first command! But Evennia doesn't know about
it yet. To tell it we have to add it to a *Command Set*.
Command Sets
------------
All commands in Evennia are always grouped together into *Command Sets*
(!CmdSets). A particular ``Command`` class definition can be part of any
number of different !CmdSets. CmdSets can be stored on game
`objects <Objects.html>`_ and on `Players <Players.html>`_ respectively.
(CmdSets). A particular ``Command`` class definition can be part of any
number of different CmdSets. CmdSets can be stored either on game
`Objects <Objects.html>`_ or on `Players <Players.html>`_.
When a user issues a command, it is matched against the contents of all
cmdsets available at the time, `merged
together <Commands#Adding<i>and</i>merging<i>command</i>sets.html>`_.
The currently valid command sets are collected from the following
sources, in this order:
cmdsets available to the user at the moment,
[Commands#Adding\_and\_merging\_command\_sets merged together]. The
currently valid command sets are collected from the following sources,
in this order:
- The active cmdset on the character object
- The cmdsets of objects carried by the character
- The cmdset of the current location
- The cmdset of objects in the currrent location (this includes exits)
- The cmdset(s) of objects in the current location (this includes
exits)
- The channel commandset
- The cmdset defined on the Player object controlling the character
(OOC cmdset)
@ -228,19 +284,32 @@ inheriting from the correct parent (``ev.CmdSet`` or
``src.commands.cmdset.CmdSet``). The CmdSet class only needs to define
one method, called ``at_cmdset_creation()``. All other class parameters
are optional, but are used for more advanced set manipulation and coding
(see the `merge rules <Commands#Merge_rules.html>`_ section).
(see the [Commands#Merge\_rules merge rules] section).
::
from ev import CmdSet from game.gamesrc.commands import mycommands class MyCmdSet(CmdSet): def at_cmdset_creation(self): """ The only thing this method should need to do is to add commands to the set. """ self.add(mycommands.MyCommand1()) self.add(mycommands.MyCommand2()) self.add(mycommands.MyCommand3())
from ev import CmdSet
from game.gamesrc.commands import mycommands
class MyCmdSet(CmdSet):
def at_cmdset_creation(self):
"""
The only thing this method should need
to do is to add commands to the set.
"""
self.add(mycommands.MyCommand1())
self.add(mycommands.MyCommand2())
self.add(mycommands.MyCommand3())
The !CmdSet's ``add()`` method can also take another CmdSet as input. In
The CmdSet's ``add()`` method can also take another CmdSet as input. In
this case all the commands from that CmdSet will be appended to this one
as if you added them line by line:
::
at_cmdset_creation(): ... self.add(AdditionalCmdSet) # adds all command from this set ...
at_cmdset_creation():
...
self.add(AdditionalCmdSet) # adds all command from this set
...
If you added your command to an existing cmdset (like to the default
cmdset), that set is already loaded into memory. You need to make the
@ -248,7 +317,7 @@ server aware of the code changes:
::
@reload
@reload
You should now be able to use the command.
@ -268,149 +337,18 @@ server, or you run
@py self.cmdset.delete('game.gamesrc.commands.mycmdset.MyCmdSet')
For more permanent addition, read the `step-by-step
guide <Commands#Adding<i>a</i>new<i>command</i>-<i>a</i>step<i>by</i>step_guide.html>`_
below. Generally you can customize which command sets are added to your
objects by using ``self.cmdset.add()`` or ``self.cmdset.add_default()``.
Adding a new command - a step by step guide
-------------------------------------------
This is a summary of the information from the previous sections. Let's
assume you have just downloaded Evennia and wants to try to add a new
command to use. This is the way to do it.
#. In ``game/gamesrc/commands``, create a new module. Name it, say,
``mycommand.py``. Copy from the templates in ``examples/`` if you
like.
#. Import ``ev.default_cmds`` and access ``MuxCommand`` from that (this
is a convenient shortcut so you don't have to import from src/
directly). The ``MuxCommand`` class handles command line parsing for
you, giving you stuff like /switches, the syntax using '=' etc). In
other words, you don't have to implement ``parse()`` on your own.
#. Create a new class in ``mycommand`` that inherits from
``MuxCommand``.
#. Set the class variable ``key`` to the name to call your command with,
say ``mycommand``.
#. Set the ``locks`` property on the command to a suitable
`lockstring <Locks#Defining<i>locks.html>`_. If you are unsure, use
"cmd:all()".
#. Define a class method ``func()`` that does stuff. See below.
#. Give your class a useful *doc*\ \_ string, this acts as the help
entry for the command.
Your command definition is now ready. Here's an example of how it could
look:
::
from ev import default_cmdsclass MyCommand(default_cmds.MuxCommand): """ Simple command example Usage: mycommand <text> This command simply echoes text back to the caller. (this string is also the help text for the command) """ key = "mycommand" locks = "cmd:all()" def func(self): "This actually does things" if not self.args: self.caller.msg("You didn't enter anything!") else: self.caller.msg("You gave the string: '%s'" % self.args)
Next we want to make this command available to us. There are many ways
to do this, but all of them involves putting this command in a *Command
Set*. First, let's try the more involved way.
#. Create a class that inherits from ``ev.CmdSet``.
#. (Optionally) set a key to name your command set.
#. Add a method ``at_cmdset_creation()`` and use the ``self.add()``
method to add your command to the command set.
This is what we have now:
::
from ev import CmdSet
from game.gamesrc.commands import mycommandclass MyCmdSet(CmdSet): key = "MyCmdSet" def at_cmdset_creation(self): self.add(mycommand.MyCommand())
This new command set could of course contain any number of commands. We
will now temporarily *merge* this command set to your current set. This
is a convenient way to test cmdsets without messing with your default
setup - if you reset those extra comands will be gone again.
Log into your game (as user #1) and add the cmdset to yourself like
this:
::
@py self.cmdset.add('game.gamesrc.commands.mycmdset.MyCmdSet')
There, you should now be able to run the ``mycommand`` command.
To remove a cmdset, use ``self.cmdset.delete()``. This will remove the
latest set (you can also name the set to remove, this where the cmdset's
key comes in handy).
You can replace an object's default command set with this command:
::
@py self.search("myobject").cmdset.add_default('game.gamesrc.commands.mycmdset.MyCmdSet')
If you want to this to survive a server shutdown, use the ``permanent``
keyword:
::
@py self.search("myobject").cmdset.add_default('game.gamesrc.commands.mycmdset.MyCmdSet', permanent=True)
The default cmdset is never affected by ``cmdset.add()`` or
``cmdset.delete()``, you have to use ``cmdset.add_default()`` to set it
explicitly (use ``remove_default()`` to remove it). Be careful to
replace or remove the default cmdset on yourself unless you really know
what you are doing - you will loose all the default Evennia commands and
might not be able to get them back ...
And finally,
::
@reload
to update the server to your changes.
Appending a new command to the default command set
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Instead of manually appending your custom cmdset you can choose to
extend Evennia's default command set directly. That way your new command
will be loaded every server start along with all the other default
commands.
The default command set is found in
``src/commands/default/cmdset_default.py`` but the template in
``gamesrc/commands/examples/`` already shows how to extend it. Copy that
file to ``game/gamesrc/`` and edit ``settings.CMDSET_DEFAULT`` to point
to this class instead. Next you add your new commands to the end of the
Default Cmdset in that file and you will in fact append it to the
existing command set.
::
# file gamesrc/commands/examples/cmdset.py ... from game.gamesrc.commands import mycommandclass DefaultSet(BaseDefaultSet): key = DefaultMUX def at_cmdset_creation(self): # this first adds all default commands super(DefaultSet, self).at_cmdset_creation() # all commands added after this point will extend or # overwrite the default commands. self.add(mycommand.MyCommand())
Again, you need to run the ``@reload`` command to make these changes
available.
Editing/replacing an existing default command
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
This works the same way as in the previous section. Just set your new
command class' ``key`` variable to be the same as that of the command to
replace. So if you want to replace the default ``look`` command, just
make sure to set your new command's ``key`` variable to ``look`` as
well.
If you want to expand/build on the original command, just copy&paste its
command class from ``src/commands/default``\ (the ``look`` class is for
example found in ``src/commands/default/general.py``) into
``game/gamesrc/commands`` and edit it there.
For more permanent addition, read the
[Commands#Adding\_a\_new\_command\_-*a\_step\_by\_step\_guide
step-by-step guide] below. Generally you can customize which command
sets are added to your objects by using ``self.cmdset.add()`` or
``self.cmdset.add_default()``.*
Adding and merging command sets
-------------------------------
*Note: This is an advanced topic. It's useful to know about, but you
\_Note: This is an advanced topic. It's useful to know about, but you
might want to skip it if this is your first time learning about
commands.*
commands.
CmdSets have the special ability that they can be *merged* together into
new sets. This would happen if you, for example, did
@ -445,7 +383,7 @@ having taken the super power-up. All this can be done on the fly by
merging command sets.
Merge rules
^^^^^^^^^^^
~~~~~~~~~~~
To understand how sets merge, we need to define a little lingo. Let's
call the first command set **A** and the second **B**. We will merge
@ -475,7 +413,8 @@ Same-key commands are merged by priority.
::
# Union A1,A2 + B1,B2,B3,B4 = A1,A2,B3,B4
# Union
A1,A2 + B1,B2,B3,B4 = A1,A2,B3,B4
**Intersect** - Only commands found in *both* cmdsets (i.e. which have
the same keys) end up in the merged cmdset, with the higher-priority
@ -483,7 +422,8 @@ cmdset replacing the lower one's commands.
::
# Intersect A1,A3,A5 + B1,B2,B4,B5 = A1,A5
# Intersect
A1,A3,A5 + B1,B2,B4,B5 = A1,A5
**Replace** - The commands of the higher-prio cmdset completely replaces
the lower-priority cmdset's commands, regardless of if same-key commands
@ -491,7 +431,8 @@ exist or not.
::
# Replace A1,A3 + B1,B2,B4,B5 = A1,A3
# Replace
A1,A3 + B1,B2,B4,B5 = A1,A3
**Remove** - The high-priority command sets removes same-key commands
from the lower-priority cmdset. They are not replaced with anything, so
@ -500,35 +441,50 @@ high-prio one as a template.
::
# Remove A1,A3 + B1,B2,B3,B4,B5 = B2,B4,B5
# Remove
A1,A3 + B1,B2,B3,B4,B5 = B2,B4,B5
Besides ``priority`` and ``mergetype``, a command set also takes a few
other variables to control how they merge:
- *allow*\ duplicates\ *(bool) - determines what happens when two sets
of equal priority merge. Default is that the new set in the merger
(i.e. **A** above) automatically takes precedence. But
if*\ allow\ *duplicates* is true, the result will be a merger with
more than one of each name match. This will usually lead to the
player receiving a multiple-match error higher up the road, but can
be good for things like cmdsets on non-player objects in a room, to
allow the system to warn that more than one 'ball' in the room has
the same 'kick' command defined on it, so it may offer a chance to
select which ball to kick ... Allowing duplicates only makes sense
for *Union* and *Intersect*, the setting is ignored for the other
- *allow\_duplicates* (bool) - determines what happens when two sets of
equal priority merge. Default is that the new set in the merger (i.e.
**A** above) automatically takes precedence. But if
*allow\_duplicates* is true, the result will be a merger with more
than one of each name match. This will usually lead to the player
receiving a multiple-match error higher up the road, but can be good
for things like cmdsets on non-player objects in a room, to allow the
system to warn that more than one 'ball' in the room has the same
'kick' command defined on it, so it may offer a chance to select
which ball to kick ... Allowing duplicates only makes sense for
*Union* and *Intersect*, the setting is ignored for the other
mergetypes.
- *key*\ mergetype\ *(dict) - allows the cmdset to define a unique
- *key\_mergetype* (dict) - allows the cmdset to define a unique
mergetype for particular cmdsets, identified by their cmdset-key.
Format is ``CmdSetkey:mergetype``. Priorities still apply. Example:
``'Myevilcmdset','Replace'`` which would make sure for this set to
always use 'Replace' on ``Myevilcmdset`` only, no matter
what*\ mergetype\_ is set to.
Format is ``{CmdSetkey:mergetype}``. Priorities still apply. Example:
``{'Myevilcmdset','Replace'}`` which would make sure for this set to
always use 'Replace' on ``Myevilcmdset`` only, no matter what
*mergetype* is set to.
More advanced cmdset example:
::
class MyCmdSet(CmdSet): key = "MyCmdSet" priority = 4 mergetype = "Replace" key_mergetype = 'MyOtherCmdSet':'Union' def at_cmdset_creation(self): """ The only thing this method should need to do is to add commands to the set. """ self.add(mycommands.MyCommand1()) self.add(mycommands.MyCommand2()) self.add(mycommands.MyCommand3())
class MyCmdSet(CmdSet):
key = "MyCmdSet"
priority = 4
mergetype = "Replace"
key_mergetype = {'MyOtherCmdSet':'Union'}
def at_cmdset_creation(self):
"""
The only thing this method should need
to do is to add commands to the set.
"""
self.add(mycommands.MyCommand1())
self.add(mycommands.MyCommand2())
self.add(mycommands.MyCommand3())
System commands
---------------
@ -539,7 +495,7 @@ learning about commands.*
There are several command-situations that are exceptional in the eyes of
the server. What happens if the player enters an empty string? What if
the 'command' given is infact the name of a channel the user wants to
send a message to? Or maybe the name of an exit they want to traverse?
send a message to? Or if there are multiple command possibilities?
Such 'special cases' are handled by what's called *system commands*. A
system command is defined in the same way as other commands, except that
@ -583,7 +539,13 @@ command must be added to a cmdset as well before it will work.
::
from ev import syscmdkeys, Commandclass MyNoInputCommand(Command): "Usage: Just press return, I dare you" key = syscmdkeys.CMD_NOINPUT def func(self): self.caller.msg("Don't just press return like that, talk to me!")
from ev import syscmdkeys, Command
class MyNoInputCommand(Command):
"Usage: Just press return, I dare you"
key = syscmdkeys.CMD_NOINPUT
def func(self):
self.caller.msg("Don't just press return like that, talk to me!")
Exits
-----
@ -618,75 +580,76 @@ Any time the user sends text to Evennia, the server tries to figure out
if the text entered corresponds to a known command. This is how the
command handler sequence looks for a logged-in user:
A user (the *caller*) enters a string of text and presses enter.
#. A user (the *caller*) enters a string of text and presses enter.
- If input is an empty string, resend command as ``CMD_NOINPUT``. If no
such command is found in cmdset, ignore.
- If command.key matches ``settings.IDLE_COMMAND``, update timers but
don't do anything more.
- If input is an empty string, resend command as ``CMD_NOINPUT``. If
no such command is found in cmdset, ignore.
- If command.key matches ``settings.IDLE_COMMAND``, update timers
but don't do anything more.
Evennia's *commandhandler* gathers the CmdSets available to *caller* at
the time:
#. Evennia's *commandhandler* gathers the CmdSets available to *caller*
at the time:
- The caller's own currently active !CmdSet.
- The active CmdSets of eventual objects in the same location (if any).
This includes commands on `Exits <Objects#Exits.html>`_.
- Sets of dynamically created *System commands* representing available
`Channels <Communications.html>`_.
- !CmdSet defined on the *caller.player* (OOC cmdset).
- The caller's own currently active CmdSet.
- The active CmdSets of eventual objects in the same location (if
any). This includes commands on [Objects#Exits Exits].
- Sets of dynamically created *System commands* representing
available `Channels <Communications.html>`_.
- CmdSet defined on the *caller.player* (OOC cmdset).
All the CmdSets are *merged* into one combined CmdSet according to each
set's merge rules.
#. All the CmdSets are *merged* into one combined CmdSet according to
each set's merge rules.
#. Evennia's *command parser* takes the merged cmdset and matches each
of its commands (using its key and aliases) against the beginning of
the string entered by *caller*. This produces a set of candidates.
#. The *cmd parser* next rates the matches by how many characters they
have and how many percent matches the respective known command. Only
if candidates cannot be separated will it return multiple matches.
Evennia's *command parser* takes the merged cmdset and matches each of
its commands (using its key and aliases) against the beginning of the
string entered by *caller*. This produces a set of candidates.
- If multiple matches were returned, resend as ``CMD_MULTIMATCH``.
If no such command is found in cmdset, return hard-coded list of
matches.
- If no match was found, resend as ``CMD_NOMATCH``. If no such
command is found in cmdset, give hard-coded error message.
The *cmd parser* next rates the matches by how many characters they have
and how many percent matches the respective known command. Only if
candidates cannot be separated will it return multiple matches.
- If multiple matches were returned, resend as ``CMD_MULTIMATCH``. If
no such command is found in cmdset, return hard-coded list of
matches.
- If no match was found, resend as ``CMD_NOMATCH``. If no such command
is found in cmdset, give hard-coded error message.
If a single command was found by the parser, the correct command class
is plucked out of storage and instantiated.
It is checked that the caller actually has access to the command by
validating the *lockstring* of the command. If not, it is not considered
as a suitable match it is resent as ``CMD_NOPERM`` is created. If no
such command is found in cmdset, use hard-coded error message.
If the new command is tagged as a channel-command, resend as
``CMD_CHANNEL``. If no such command is found in cmdset, use hard-coded
implementation.
Assign several useful variables to the command instance.
Call ``at_pre_command()`` on the command instance.
Call ``parse()`` on the command instance. This is is fed the remainder
of the string, after the name of the command. It's intended to pre-parse
the string int a form useful for the ``func()`` method.
Call ``func()`` on the command instance. This is the functional body of
the command, actually doing useful things.
Call ``at_post_command()`` on the command instance.
#. If a single command was found by the parser, the correct command
class is plucked out of storage and instantiated.
#. It is checked that the caller actually has access to the command by
validating the *lockstring* of the command. If not, it is not
considered as a suitable match it is resent as ``CMD_NOPERM`` is
created. If no such command is found in cmdset, use hard-coded error
message.
#. If the new command is tagged as a channel-command, resend as
``CMD_CHANNEL``. If no such command is found in cmdset, use
hard-coded implementation.
#. Assign several useful variables to the command instance.
#. Call ``at_pre_command()`` on the command instance.
#. Call ``parse()`` on the command instance. This is is fed the
remainder of the string, after the name of the command. It's intended
to pre-parse the string int a form useful for the ``func()`` method.
#. Call ``func()`` on the command instance. This is the functional body
of the command, actually doing useful things.
#. Call ``at_post_command()`` on the command instance.
Assorted notes
--------------
The return value of ``Command.func()`` *is* safely passed on should one
have some very specific use case in mind. So one could in principle do
``value = obj.execute_cmd(cmdname)``. Evennia does not use this
functionality at all by default (all default commands simply returns
``None``) and it's probably not relevant to any but the most
advanced/exotic designs (one might use it to create a "nested" command
structure for example).
The return value of ``Command.func()`` is a Twisted
`deferred <http://twistedmatrix.com/documents/current/core/howto/defer.html>`_.
Evennia does not use this return value at all by default. If you do, you
must thus do so asychronously, using callbacks.
::
# in command class func()
def callback(ret, caller):
caller.msg("Returned is %s" % ret)
deferred = self.execute_command("longrunning")
deferred.addCallback(callback, self.caller)
This is probably not relevant to any but the most advanced/exotic
designs (one might use it to create a "nested" command structure for
example).
The ``save_for_next`` class variable can be used to implement
state-persistent commands. For example it can make a command operate on