evennia/docs/latest/Howtos/Beginner-Tutorial/Part1/Beginner-Tutorial-Adding-Commands.html
Evennia docbuilder action d17f22fc2c Updated HTML docs.
2024-03-17 13:48:03 +00:00

575 lines
No EOL
51 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" /><meta name="generator" content="Docutils 0.17.1: http://docutils.sourceforge.net/" />
<title>8. Adding custom commands &#8212; Evennia latest documentation</title>
<link rel="stylesheet" href="../../../_static/nature.css" type="text/css" />
<link rel="stylesheet" href="../../../_static/pygments.css" type="text/css" />
<script id="documentation_options" data-url_root="../../../" src="../../../_static/documentation_options.js"></script>
<script src="../../../_static/jquery.js"></script>
<script src="../../../_static/underscore.js"></script>
<script src="../../../_static/doctools.js"></script>
<script src="../../../_static/language_data.js"></script>
<link rel="shortcut icon" href="../../../_static/favicon.ico"/>
<link rel="index" title="Index" href="../../../genindex.html" />
<link rel="search" title="Search" href="../../../search.html" />
<link rel="next" title="9. Parsing Command input" href="Beginner-Tutorial-More-on-Commands.html" />
<link rel="prev" title="7. Making objects persistent" href="Beginner-Tutorial-Learning-Typeclasses.html" />
</head><body>
<div class="related" role="navigation" aria-label="related navigation">
<h3>Navigation</h3>
<ul>
<li class="right" style="margin-right: 10px">
<a href="../../../genindex.html" title="General Index"
accesskey="I">index</a></li>
<li class="right" >
<a href="../../../py-modindex.html" title="Python Module Index"
>modules</a> |</li>
<li class="right" >
<a href="Beginner-Tutorial-More-on-Commands.html" title="9. Parsing Command input"
accesskey="N">next</a> |</li>
<li class="right" >
<a href="Beginner-Tutorial-Learning-Typeclasses.html" title="7. Making objects persistent"
accesskey="P">previous</a> |</li>
<li class="nav-item nav-item-0"><a href="../../../index.html">Evennia latest</a> &#187;</li>
<li class="nav-item nav-item-1"><a href="../../Howtos-Overview.html" >Tutorials and How-Tos</a> &#187;</li>
<li class="nav-item nav-item-2"><a href="../Beginner-Tutorial-Overview.html" >Beginner Tutorial</a> &#187;</li>
<li class="nav-item nav-item-3"><a href="Beginner-Tutorial-Part1-Overview.html" accesskey="U">Part 1: What We Have</a> &#187;</li>
<li class="nav-item nav-item-this"><a href=""><span class="section-number">8. </span>Adding custom commands</a></li>
</ul>
</div>
<div class="document">
<div class="documentwrapper">
<div class="sphinxsidebar" role="navigation" aria-label="main navigation">
<div class="sphinxsidebarwrapper">
<p class="logo"><a href="../../../index.html">
<img class="logo" src="../../../_static/evennia_logo.png" alt="Logo"/>
</a></p>
<div id="searchbox" style="display: none" role="search">
<h3 id="searchlabel">Quick search</h3>
<div class="searchformwrapper">
<form class="search" action="../../../search.html" method="get">
<input type="text" name="q" aria-labelledby="searchlabel" />
<input type="submit" value="Go" />
</form>
</div>
</div>
<script>$('#searchbox').show(0);</script>
<h3><a href="../../../index.html">Table of Contents</a></h3>
<ul>
<li><a class="reference internal" href="#">8. Adding custom commands</a><ul>
<li><a class="reference internal" href="#creating-a-custom-command">8.1. Creating a custom command</a><ul>
<li><a class="reference internal" href="#making-our-cmdset-persistent">8.1.1. Making our cmdset persistent</a></li>
<li><a class="reference internal" href="#add-the-echo-command-to-the-default-cmdset">8.1.2. Add the echo command to the default cmdset</a></li>
<li><a class="reference internal" href="#figuring-out-who-to-hit">8.1.3. Figuring out who to hit</a></li>
</ul>
</li>
<li><a class="reference internal" href="#summary">8.2. Summary</a></li>
</ul>
</li>
</ul>
<h4>Previous topic</h4>
<p class="topless"><a href="Beginner-Tutorial-Learning-Typeclasses.html"
title="previous chapter"><span class="section-number">7. </span>Making objects persistent</a></p>
<h4>Next topic</h4>
<p class="topless"><a href="Beginner-Tutorial-More-on-Commands.html"
title="next chapter"><span class="section-number">9. </span>Parsing Command input</a></p>
<div role="note" aria-label="source link">
<!--h3>This Page</h3-->
<ul class="this-page-menu">
<li><a href="../../../_sources/Howtos/Beginner-Tutorial/Part1/Beginner-Tutorial-Adding-Commands.md.txt"
rel="nofollow">Show Page Source</a></li>
</ul>
</div><h3>Links</h3>
<ul>
<li><a href="https://www.evennia.com/docs/latest/index.html">Documentation Top</a> </li>
<li><a href="https://www.evennia.com">Evennia Home</a> </li>
<li><a href="https://github.com/evennia/evennia">Github</a> </li>
<li><a href="http://games.evennia.com">Game Index</a> </li>
<li>
<a href="https://discord.gg/AJJpcRUhtF">Discord</a> -
<a href="https://github.com/evennia/evennia/discussions">Discussions</a> -
<a href="https://evennia.blogspot.com/">Blog</a>
</li>
</ul>
<h3>Doc Versions</h3>
<ul>
<li><a href="Beginner-Tutorial-Adding-Commands.html">latest (main branch)</a></li>
<li><a href="../4.x/index.html">v4.0.0 branch (outdated)</a></li>
<li><a href="../3.x/index.html">v3.0.0 branch (outdated)</a></li>
<li><a href="../2.x/index.html">v2.0.0 branch (outdated)</a></li>
<li><a href="../1.x/index.html">v1.0.0 branch (outdated)</a></li>
<li><a href="../0.x/index.html">v0.9.5 branch (outdated)</a></li>
</ul>
</div>
</div>
<div class="bodywrapper">
<div class="body" role="main">
<section class="tex2jax_ignore mathjax_ignore" id="adding-custom-commands">
<h1><span class="section-number">8. </span>Adding custom commands<a class="headerlink" href="#adding-custom-commands" title="Permalink to this headline"></a></h1>
<p>In this lesson well learn how to create our own Evennia <a class="reference internal" href="../../../Components/Commands.html"><span class="doc std std-doc">Commands</span></a> If you are new to Python youll also learn some more basics about how to manipulate strings and get information out of Evennia.</p>
<p>A Command is something that handles the input from a user and causes a result to happen.
An example is <code class="docutils literal notranslate"><span class="pre">look</span></code>, which examines your current location and tells you what it looks like and
what is in it.</p>
<aside class="sidebar">
<p class="sidebar-title">Commands are not typeclassed</p>
<p>If you just came from the previous lesson, you might want to know that Commands and CommandSets are not <code class="docutils literal notranslate"><span class="pre">typeclassed</span></code>. That is, instances of them are not saved to the database. They are “just” normal Python classes.</p>
</aside>
<p>In Evennia, a Command is a Python <em>class</em>. If you are unsure about what a class is, review the
<a class="reference internal" href="Beginner-Tutorial-Python-classes-and-objects.html"><span class="doc std std-doc">previous lesson about it</span></a>! A Command inherits from <code class="docutils literal notranslate"><span class="pre">evennia.Command</span></code> or from one of the alternative command- classes, such as <code class="docutils literal notranslate"><span class="pre">MuxCommand</span></code> which is what most default commands use.</p>
<p>All Commands are grouped in another class called a <em>Command Set</em>. Think of a Command Set as a bag holding many different commands. One CmdSet could for example hold all commands for combat, another for building etc.</p>
<p>Command-Sets are then associated with objects, for example with your Character. Doing so makes the commands in that cmdset available to the object. By default, Evennia groups all character-commands into one big cmdset called the <code class="docutils literal notranslate"><span class="pre">CharacterCmdSet</span></code>. It sits on <code class="docutils literal notranslate"><span class="pre">DefaultCharacter</span></code> (and thus, through inheritance, on <code class="docutils literal notranslate"><span class="pre">typeclasses.characters.Character</span></code>).</p>
<section id="creating-a-custom-command">
<h2><span class="section-number">8.1. </span>Creating a custom command<a class="headerlink" href="#creating-a-custom-command" title="Permalink to this headline"></a></h2>
<p>Open <code class="docutils literal notranslate"><span class="pre">mygame/commands/command.py</span></code>:</p>
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="sd">&quot;&quot;&quot;</span>
<span class="sd">(module docstring)</span>
<span class="sd">&quot;&quot;&quot;</span>
<span class="kn">from</span> <span class="nn">evennia</span> <span class="kn">import</span> <span class="n">Command</span> <span class="k">as</span> <span class="n">BaseCommand</span>
<span class="c1"># from evennia import default_cmds</span>
<span class="k">class</span> <span class="nc">Command</span><span class="p">(</span><span class="n">BaseCommand</span><span class="p">):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;</span>
<span class="sd"> (class docstring)</span>
<span class="sd"> &quot;&quot;&quot;</span>
<span class="k">pass</span>
<span class="c1"># (lots of commented-out stuff)</span>
<span class="c1"># ...</span>
</pre></div>
</div>
<p>Ignoring the docstrings (which you can read if you want), this is the only really active code in the module.</p>
<p>We can see that we import <code class="docutils literal notranslate"><span class="pre">Command</span></code> from <code class="docutils literal notranslate"><span class="pre">evennia</span></code> and use the <code class="docutils literal notranslate"><span class="pre">from</span> <span class="pre">...</span> <span class="pre">import</span> <span class="pre">...</span> <span class="pre">as</span> <span class="pre">...</span></code> form to rename it to <code class="docutils literal notranslate"><span class="pre">BaseCommand</span></code>. This is so we can let our child class also be named <code class="docutils literal notranslate"><span class="pre">Command</span></code> to make it easier to reference. The class itself doesnt do anything, it just has <code class="docutils literal notranslate"><span class="pre">pass</span></code>. So in the same way as <code class="docutils literal notranslate"><span class="pre">Object</span></code> and <code class="docutils literal notranslate"><span class="pre">Character</span></code> in the previous lessons, this class is identical to its parent.</p>
<blockquote>
<div><p>The commented out <code class="docutils literal notranslate"><span class="pre">default_cmds</span></code> gives us access to Evennias default commands for easy overriding. Well try that a little later.</p>
</div></blockquote>
<p>We could modify this module directly, but lets work in a separate module just for the heck of it. Open a new file <code class="docutils literal notranslate"><span class="pre">mygame/commands/mycommands.py</span></code> and add the following code:</p>
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="c1"># in mygame/commands/mycommands.py</span>
<span class="kn">from</span> <span class="nn">commands.command</span> <span class="kn">import</span> <span class="n">Command</span>
<span class="k">class</span> <span class="nc">CmdEcho</span><span class="p">(</span><span class="n">Command</span><span class="p">):</span>
<span class="n">key</span> <span class="o">=</span> <span class="s2">&quot;echo&quot;</span>
</pre></div>
</div>
<p>This is the simplest form of command you can imagine. It just gives itself a name, “echo”. This is what you will use to call this command later.</p>
<p>Next we need to put this in a CmdSet. It will be a one-command CmdSet for now! Change your file as such:</p>
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="c1"># in mygame/commands/mycommands.py</span>
<span class="kn">from</span> <span class="nn">commands.command</span> <span class="kn">import</span> <span class="n">Command</span>
<span class="kn">from</span> <span class="nn">evennia</span> <span class="kn">import</span> <span class="n">CmdSet</span>
<span class="k">class</span> <span class="nc">CmdEcho</span><span class="p">(</span><span class="n">Command</span><span class="p">):</span>
<span class="n">key</span> <span class="o">=</span> <span class="s2">&quot;echo&quot;</span>
<span class="k">class</span> <span class="nc">MyCmdSet</span><span class="p">(</span><span class="n">CmdSet</span><span class="p">):</span>
<span class="k">def</span> <span class="nf">at_cmdset_creation</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="bp">self</span><span class="o">.</span><span class="n">add</span><span class="p">(</span><span class="n">CmdEcho</span><span class="p">)</span>
</pre></div>
</div>
<p>Our <code class="docutils literal notranslate"><span class="pre">MyCmdSet</span></code> class must have an <code class="docutils literal notranslate"><span class="pre">at_cmdset_creation</span></code> method, named exactly like this - this is what Evennia will be looking for when setting up the cmdset later, so if you didnt set it up, it will use the parents version, which is empty. Inside we add the command class to the cmdset by <code class="docutils literal notranslate"><span class="pre">self.add()</span></code>. If you wanted to add more commands to this CmdSet you could just add more lines of <code class="docutils literal notranslate"><span class="pre">self.add</span></code> after this.</p>
<p>Finally, lets add this command to ourselves so we can try it out. In-game you can experiment with <code class="docutils literal notranslate"><span class="pre">py</span></code> again:</p>
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>&gt; py me.cmdset.add(&quot;commands.mycommands.MyCmdSet&quot;)
</pre></div>
</div>
<p>The <code class="docutils literal notranslate"><span class="pre">me.cmdset</span></code> is the store of all cmdsets stored on us. By giving the path to our CmdSet class, it will be added.</p>
<p>Now try</p>
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>&gt; echo
Command echo has no defined `func()` - showing on-command variables:
...
...
</pre></div>
</div>
<p><code class="docutils literal notranslate"><span class="pre">echo</span></code> works! You should be getting a long list of outputs. The reason for this is that your <code class="docutils literal notranslate"><span class="pre">echo</span></code> function is not really “doing” anything yet and the default function is then to show all useful resources available to you when you use your Command. Lets look at some of those listed:</p>
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>Command echo has no defined `func()` - showing on-command variables:
obj (&lt;class &#39;typeclasses.characters.Character&#39;&gt;): YourName
lockhandler (&lt;class &#39;evennia.locks.lockhandler.LockHandler&#39;&gt;): cmd:all()
caller (&lt;class &#39;typeclasses.characters.Character&#39;&gt;): YourName
cmdname (&lt;class &#39;str&#39;&gt;): echo
raw_cmdname (&lt;class &#39;str&#39;&gt;): echo
cmdstring (&lt;class &#39;str&#39;&gt;): echo
args (&lt;class &#39;str&#39;&gt;):
cmdset (&lt;class &#39;evennia.commands.cmdset.CmdSet&#39;&gt;): @mail, about, access, accounts, addcom, alias, allcom, ban, batchcode, batchcommands, boot, cboot, ccreate,
cdesc, cdestroy, cemit, channels, charcreate, chardelete, checklockstring, clientwidth, clock, cmdbare, cmdsets, color, copy, cpattr, create, cwho, delcom,
desc, destroy, dig, dolphin, drop, echo, emit, examine, find, force, get, give, grapevine2chan, help, home, ic, inventory, irc2chan, ircstatus, link, lock,
look, menutest, mudinfo, mvattr, name, nick, objects, ooc, open, option, page, password, perm, pose, public, py, quell, quit, reload, reset, rss2chan, say,
script, scripts, server, service, sessions, set, setdesc, sethelp, sethome, shutdown, spawn, style, tag, tel, test2010, test2028, testrename, testtable,
tickers, time, tunnel, typeclass, unban, unlink, up, up, userpassword, wall, whisper, who, wipe
session (&lt;class &#39;evennia.server.serversession.ServerSession&#39;&gt;): Griatch(#1)@1:2:7:.:0:.:0:.:1
account (&lt;class &#39;typeclasses.accounts.Account&#39;&gt;): Griatch(account 1)
raw_string (&lt;class &#39;str&#39;&gt;): echo
--------------------------------------------------
echo - Command variables from evennia:
--------------------------------------------------
name of cmd (self.key): echo
cmd aliases (self.aliases): []
cmd locks (self.locks): cmd:all();
help category (self.help_category): General
object calling (self.caller): Griatch
object storing cmdset (self.obj): Griatch
command string given (self.cmdstring): echo
current cmdset (self.cmdset): ChannelCmdSet
</pre></div>
</div>
<p>These are all properties you can access with <code class="docutils literal notranslate"><span class="pre">.</span></code> on the Command instance, such as <code class="docutils literal notranslate"><span class="pre">.key</span></code>, <code class="docutils literal notranslate"><span class="pre">.args</span></code> and so on. Evennia makes these available to you and they will be different every time a command is run. The most important ones we will make use of now are:</p>
<ul class="simple">
<li><p><code class="docutils literal notranslate"><span class="pre">caller</span></code> - this is you, the person calling the command.</p></li>
<li><p><code class="docutils literal notranslate"><span class="pre">args</span></code> - this is all arguments to the command. Now its empty, but if you tried <code class="docutils literal notranslate"><span class="pre">echo</span> <span class="pre">foo</span> <span class="pre">bar</span></code> youd find that this would be <code class="docutils literal notranslate"><span class="pre">&quot;</span> <span class="pre">foo</span> <span class="pre">bar&quot;</span></code>.</p></li>
<li><p><code class="docutils literal notranslate"><span class="pre">obj</span></code> - this is object on which this Command (and CmdSet) “sits”. So you, in this case.</p></li>
</ul>
<p>The reason our command doesnt do anything yet is because its missing a <code class="docutils literal notranslate"><span class="pre">func</span></code> method. This is what Evennia looks for to figure out what a Command actually does. Modify your <code class="docutils literal notranslate"><span class="pre">CmdEcho</span></code> class:</p>
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="c1"># in mygame/commands/mycommands.py</span>
<span class="c1"># ...</span>
<span class="k">class</span> <span class="nc">CmdEcho</span><span class="p">(</span><span class="n">Command</span><span class="p">):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;</span>
<span class="sd"> A simple echo command</span>
<span class="sd"> Usage:</span>
<span class="sd"> echo &lt;something&gt;</span>
<span class="sd"> &quot;&quot;&quot;</span>
<span class="n">key</span> <span class="o">=</span> <span class="s2">&quot;echo&quot;</span>
<span class="k">def</span> <span class="nf">func</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="bp">self</span><span class="o">.</span><span class="n">caller</span><span class="o">.</span><span class="n">msg</span><span class="p">(</span><span class="sa">f</span><span class="s2">&quot;Echo: &#39;</span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">args</span><span class="si">}</span><span class="s2">&#39;&quot;</span><span class="p">)</span>
<span class="c1"># ...</span>
</pre></div>
</div>
<p>First we added a docstring. This is always a good thing to do in general, but for a Command class, it will also automatically become the in-game help entry!</p>
<aside class="sidebar">
<p class="sidebar-title">Use Command.msg </p>
<p>In a Command class, the <code class="docutils literal notranslate"><span class="pre">self.msg()</span></code> acts as a convenient shortcut for <code class="docutils literal notranslate"><span class="pre">self.caller.msg()</span></code>. Not only is it shorter, it also has some advantages because the command can include more metadata with the message. So using <code class="docutils literal notranslate"><span class="pre">self.msg()</span></code> is usually better. For this tutorial though, <code class="docutils literal notranslate"><span class="pre">self.caller.msg()</span></code> is more explicit in showing what is going on.</p>
</aside>
<p>Next we add the <code class="docutils literal notranslate"><span class="pre">func</span></code> method. It has one active line where it makes use of some of those variables the Command class offers to us. If you did the <a class="reference internal" href="Beginner-Tutorial-Python-basic-introduction.html"><span class="doc std std-doc">basic Python tutorial</span></a>, you will recognize <code class="docutils literal notranslate"><span class="pre">.msg</span></code> - this will send a message to the object it is attached to us - in this case <code class="docutils literal notranslate"><span class="pre">self.caller</span></code>, that is, us. We grab <code class="docutils literal notranslate"><span class="pre">self.args</span></code> and includes that in the message.</p>
<p>Since we havent changed <code class="docutils literal notranslate"><span class="pre">MyCmdSet</span></code>, that will work as before. Reload and re-add this command to ourselves to try out the new version:</p>
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>&gt; reload
&gt; py self.cmdset.add(&quot;commands.mycommands.MyCmdSet&quot;)
&gt; echo
Echo: &#39;&#39;
</pre></div>
</div>
<p>Try to pass an argument:</p>
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>&gt; echo Woo Tang!
Echo: &#39; Woo Tang!&#39;
</pre></div>
</div>
<p>Note that there is an extra space before <code class="docutils literal notranslate"><span class="pre">Woo</span></code>. That is because self.args contains <em>everything</em> after the command name, including spaces. Lets remove that extra space with a small tweak:</p>
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="c1"># in mygame/commands/mycommands.py</span>
<span class="c1"># ...</span>
<span class="k">class</span> <span class="nc">CmdEcho</span><span class="p">(</span><span class="n">Command</span><span class="p">):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;</span>
<span class="sd"> A simple echo command</span>
<span class="sd"> Usage:</span>
<span class="sd"> echo &lt;something&gt;</span>
<span class="sd"> &quot;&quot;&quot;</span>
<span class="n">key</span> <span class="o">=</span> <span class="s2">&quot;echo&quot;</span>
<span class="k">def</span> <span class="nf">func</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="bp">self</span><span class="o">.</span><span class="n">caller</span><span class="o">.</span><span class="n">msg</span><span class="p">(</span><span class="sa">f</span><span class="s2">&quot;Echo: &#39;</span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">args</span><span class="o">.</span><span class="n">strip</span><span class="p">()</span><span class="si">}</span><span class="s2">&#39;&quot;</span><span class="p">)</span>
<span class="c1"># ...</span>
</pre></div>
</div>
<p>The only difference is that we called <code class="docutils literal notranslate"><span class="pre">.strip()</span></code> on <code class="docutils literal notranslate"><span class="pre">self.args</span></code>. This is a helper method available on all strings - it strips out all whitespace before and after the string. Now the Command-argument will no longer have any space in front of it.</p>
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>&gt; reload
&gt; py self.cmdset.add(&quot;commands.mycommands.MyCmdSet&quot;)
&gt; echo Woo Tang!
Echo: &#39;Woo Tang!&#39;
</pre></div>
</div>
<p>Dont forget to look at the help for the echo command:</p>
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>&gt; help echo
</pre></div>
</div>
<p>You will get the docstring you put in your Command-class!</p>
<section id="making-our-cmdset-persistent">
<h3><span class="section-number">8.1.1. </span>Making our cmdset persistent<a class="headerlink" href="#making-our-cmdset-persistent" title="Permalink to this headline"></a></h3>
<p>Its getting a little annoying to have to re-add our cmdset every time we reload, right? Its simple enough to make <code class="docutils literal notranslate"><span class="pre">echo</span></code> a <em>persistent</em> change though:</p>
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>&gt; py self.cmdset.add(&quot;commands.mycommands.MyCmdSet&quot;, persistent=True)
</pre></div>
</div>
<p>Now you can <code class="docutils literal notranslate"><span class="pre">reload</span></code> as much as you want and your code changes will be available directly without needing to re-add the MyCmdSet again.</p>
<p>We will add this cmdset in another way, so remove it manually:</p>
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>&gt; py self.cmdset.remove(&quot;commands.mycommands.MyCmdSet&quot;)
</pre></div>
</div>
</section>
<section id="add-the-echo-command-to-the-default-cmdset">
<h3><span class="section-number">8.1.2. </span>Add the echo command to the default cmdset<a class="headerlink" href="#add-the-echo-command-to-the-default-cmdset" title="Permalink to this headline"></a></h3>
<p>Above we added the <code class="docutils literal notranslate"><span class="pre">echo</span></code> command to ourselves. It will <em>only</em> be available to us and noone else in the game. But all commands in Evennia are part of command-sets, including the normal <code class="docutils literal notranslate"><span class="pre">look</span></code> and <code class="docutils literal notranslate"><span class="pre">py</span></code> commands we have been using all the while. You can easily extend the default command set with your <code class="docutils literal notranslate"><span class="pre">echo</span></code> command - this way <em>everyone</em> in the game will have access to it!</p>
<p>In <code class="docutils literal notranslate"><span class="pre">mygame/commands/</span></code> youll find an existing module named <code class="docutils literal notranslate"><span class="pre">default_cmdsets.py</span></code> Open it and youll find four empty cmdset-classes:</p>
<ul class="simple">
<li><p><code class="docutils literal notranslate"><span class="pre">CharacterCmdSet</span></code> - this sits on all Characters (this is the one we usually want to modify)</p></li>
<li><p><code class="docutils literal notranslate"><span class="pre">AccountCmdSet</span></code> - this sits on all Accounts (shared between Characters, like <code class="docutils literal notranslate"><span class="pre">logout</span></code> etc)</p></li>
<li><p><code class="docutils literal notranslate"><span class="pre">UnloggedCmdSet</span></code> - commands available <em>before</em> you login, like the commands for creating your password and connecting to the game.</p></li>
<li><p><code class="docutils literal notranslate"><span class="pre">SessionCmdSet</span></code> - commands unique to your Session (your particular client connection). This is unused by default.</p></li>
</ul>
<p>Tweak this file as follows:</p>
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="c1"># in mygame/commands/default_cmdsets.py </span>
<span class="c1"># ,.. </span>
<span class="kn">from</span> <span class="nn">.</span> <span class="kn">import</span> <span class="n">mycommands</span> <span class="c1"># &lt;------- </span>
<span class="k">class</span> <span class="nc">CharacterCmdSet</span><span class="p">(</span><span class="n">default_cmds</span><span class="o">.</span><span class="n">CharacterCmdSet</span><span class="p">):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;</span>
<span class="sd"> The `CharacterCmdSet` contains general in-game commands like `look`,</span>
<span class="sd"> `get`, etc available on in-game Character objects. It is merged with</span>
<span class="sd"> the `AccountCmdSet` when an Account puppets a Character.</span>
<span class="sd"> &quot;&quot;&quot;</span>
<span class="n">key</span> <span class="o">=</span> <span class="s2">&quot;DefaultCharacter&quot;</span>
<span class="k">def</span> <span class="nf">at_cmdset_creation</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;</span>
<span class="sd"> Populates the cmdset</span>
<span class="sd"> &quot;&quot;&quot;</span>
<span class="nb">super</span><span class="p">()</span><span class="o">.</span><span class="n">at_cmdset_creation</span><span class="p">()</span>
<span class="c1">#</span>
<span class="c1"># any commands you add below will overload the default ones.</span>
<span class="c1">#</span>
<span class="bp">self</span><span class="o">.</span><span class="n">add</span><span class="p">(</span><span class="n">mycommands</span><span class="o">.</span><span class="n">CmdEcho</span><span class="p">)</span> <span class="c1"># &lt;-----------</span>
<span class="c1"># ... </span>
</pre></div>
</div>
<aside class="sidebar">
<p class="sidebar-title">super() and overriding defaults</p>
<p>The <code class="docutils literal notranslate"><span class="pre">super()</span></code> Python keyword means that the <em>parent</em> is called. In this case, the parent adds all default commands to this cmdset.</p>
<p>Coincidentally, this is also how you replace default commands in Evennia!jj To replace e.g. the command <code class="docutils literal notranslate"><span class="pre">get</span></code>, you just give your replacement command the <code class="docutils literal notranslate"><span class="pre">key</span></code> get and add it here - since its added after <code class="docutils literal notranslate"><span class="pre">super()</span></code>, it will replace the default version of <code class="docutils literal notranslate"><span class="pre">get</span></code>.</p>
</aside>
<p>This works the same way as when you added <code class="docutils literal notranslate"><span class="pre">CmdEcho</span></code> to your <code class="docutils literal notranslate"><span class="pre">MyCmdSet</span></code>. The only difference cmdsets are automatically added to all Characters/Accounts etc so you dont have to do so manually. We must also make sure to import the <code class="docutils literal notranslate"><span class="pre">CmdEcho</span></code> from your <code class="docutils literal notranslate"><span class="pre">mycommands</span></code> module in order for this module to know about it. The period <code class="docutils literal notranslate"><span class="pre">.</span></code> in <code class="docutils literal notranslate"><span class="pre">from</span> <span class="pre">.</span> <span class="pre">import</span> <span class="pre">mycommands</span></code> means that we are telling Python that <code class="docutils literal notranslate"><span class="pre">mycommands.py</span></code> sits in the same directory as this current module. We want to import the entire module. Further down we access <code class="docutils literal notranslate"><span class="pre">mycommands.CmdEcho</span></code> to add it to the character cmdset.</p>
<p>Just <code class="docutils literal notranslate"><span class="pre">reload</span></code> the server and your <code class="docutils literal notranslate"><span class="pre">echo</span></code> command will be available again. There is no limit to how many cmdsets a given Command can be a part of.</p>
<p>To remove, you just comment out or delete the <code class="docutils literal notranslate"><span class="pre">self.add()</span></code> line. Keep it like this for now though - well expand on it below.</p>
</section>
<section id="figuring-out-who-to-hit">
<h3><span class="section-number">8.1.3. </span>Figuring out who to hit<a class="headerlink" href="#figuring-out-who-to-hit" title="Permalink to this headline"></a></h3>
<p>Lets try something a little more exciting than just echo. Lets make a <code class="docutils literal notranslate"><span class="pre">hit</span></code> command, for punching someone in the face! This is how we want it to work:</p>
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>&gt; hit &lt;target&gt;
You hit &lt;target&gt; with full force!
</pre></div>
</div>
<p>Not only that, we want the <code class="docutils literal notranslate"><span class="pre">&lt;target&gt;</span></code> to see</p>
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>You got hit by &lt;hitter&gt; with full force!
</pre></div>
</div>
<p>Here, <code class="docutils literal notranslate"><span class="pre">&lt;hitter&gt;</span></code> would be the one using the <code class="docutils literal notranslate"><span class="pre">hit</span></code> command and <code class="docutils literal notranslate"><span class="pre">&lt;target&gt;</span></code> is the one doing the punching; so if your name was <code class="docutils literal notranslate"><span class="pre">Anna</span></code>, and you hit someone named <code class="docutils literal notranslate"><span class="pre">Bob</span></code>, this would look like this:</p>
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>&gt; hit bob
You hit Bob with full force!
</pre></div>
</div>
<p>And Bob would see</p>
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>You got hit by by Anna with full force!
</pre></div>
</div>
<p>Still in <code class="docutils literal notranslate"><span class="pre">mygame/commands/mycommands.py</span></code>, add a new class, between <code class="docutils literal notranslate"><span class="pre">CmdEcho</span></code> and <code class="docutils literal notranslate"><span class="pre">MyCmdSet</span></code>.</p>
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="c1"># in mygame/commands/mycommands.py</span>
<span class="p">:</span><span class="n">linenos</span><span class="p">:</span>
<span class="p">:</span><span class="n">emphasize</span><span class="o">-</span><span class="n">lines</span><span class="p">:</span> <span class="mi">3</span><span class="p">,</span><span class="mi">4</span><span class="p">,</span><span class="mi">11</span><span class="p">,</span><span class="mi">14</span><span class="p">,</span><span class="mi">15</span><span class="p">,</span><span class="mi">17</span><span class="p">,</span><span class="mi">18</span><span class="p">,</span><span class="mi">19</span><span class="p">,</span><span class="mi">21</span>
<span class="c1"># ...</span>
<span class="k">class</span> <span class="nc">CmdHit</span><span class="p">(</span><span class="n">Command</span><span class="p">):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;</span>
<span class="sd"> Hit a target.</span>
<span class="sd"> Usage:</span>
<span class="sd"> hit &lt;target&gt;</span>
<span class="sd"> &quot;&quot;&quot;</span>
<span class="n">key</span> <span class="o">=</span> <span class="s2">&quot;hit&quot;</span>
<span class="k">def</span> <span class="nf">func</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="n">args</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">args</span><span class="o">.</span><span class="n">strip</span><span class="p">()</span>
<span class="k">if</span> <span class="ow">not</span> <span class="n">args</span><span class="p">:</span>
<span class="bp">self</span><span class="o">.</span><span class="n">caller</span><span class="o">.</span><span class="n">msg</span><span class="p">(</span><span class="s2">&quot;Who do you want to hit?&quot;</span><span class="p">)</span>
<span class="k">return</span>
<span class="n">target</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">caller</span><span class="o">.</span><span class="n">search</span><span class="p">(</span><span class="n">args</span><span class="p">)</span>
<span class="k">if</span> <span class="ow">not</span> <span class="n">target</span><span class="p">:</span>
<span class="k">return</span>
<span class="bp">self</span><span class="o">.</span><span class="n">caller</span><span class="o">.</span><span class="n">msg</span><span class="p">(</span><span class="sa">f</span><span class="s2">&quot;You hit </span><span class="si">{</span><span class="n">target</span><span class="o">.</span><span class="n">key</span><span class="si">}</span><span class="s2"> with full force!&quot;</span><span class="p">)</span>
<span class="n">target</span><span class="o">.</span><span class="n">msg</span><span class="p">(</span><span class="sa">f</span><span class="s2">&quot;You got hit by </span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">caller</span><span class="o">.</span><span class="n">key</span><span class="si">}</span><span class="s2"> with full force!&quot;</span><span class="p">)</span>
<span class="c1"># ...</span>
</pre></div>
</div>
<p>A lot of things to dissect here:</p>
<ul class="simple">
<li><p><strong>Line 3</strong>: The normal <code class="docutils literal notranslate"><span class="pre">class</span></code> header. We inherit from <code class="docutils literal notranslate"><span class="pre">Command</span></code> which we imported at the top of this file.</p></li>
<li><p><strong>Lines 4-10</strong>: The docstring and help-entry for the command. You could expand on this as much as you wanted.</p></li>
<li><p><strong>Line 11</strong>: We want to write <code class="docutils literal notranslate"><span class="pre">hit</span></code> to use this command.</p></li>
<li><p><strong>Line 14</strong>: We strip the whitespace from the argument like before. Since we dont want to have to do <code class="docutils literal notranslate"><span class="pre">self.args.strip()</span></code> over and over, we store the stripped version in a <em>local variable</em> <code class="docutils literal notranslate"><span class="pre">args</span></code>. Note that we dont modify <code class="docutils literal notranslate"><span class="pre">self.args</span></code> by doing this, <code class="docutils literal notranslate"><span class="pre">self.args</span></code> will still have the whitespace and is not the same as <code class="docutils literal notranslate"><span class="pre">args</span></code> in this example.</p></li>
</ul>
<aside class="sidebar">
<p class="sidebar-title">if-statements</p>
<p>The full form of the if statement is</p>
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>if condition:
...
elif othercondition:
...
else:
...
</pre></div>
</div>
<p>There can be any number of <code class="docutils literal notranslate"><span class="pre">elifs</span></code> to mark when different branches of the code should run. If <code class="docutils literal notranslate"><span class="pre">else</span></code> is provided, it will run if none of the other conditions were truthy.</p>
</aside>
<ul class="simple">
<li><p><strong>Line 15</strong> has our first <em>conditional</em>, an <code class="docutils literal notranslate"><span class="pre">if</span></code> statement. This is written on the form <code class="docutils literal notranslate"><span class="pre">if</span> <span class="pre">&lt;condition&gt;:</span></code> and only if that condition is truthy will the indented code block under the <code class="docutils literal notranslate"><span class="pre">if</span></code> statement run. To learn what is truthy in Python its usually easier to learn what is “falsy”:</p>
<ul>
<li><p><code class="docutils literal notranslate"><span class="pre">False</span></code> - this is a reserved boolean word in Python. The opposite is <code class="docutils literal notranslate"><span class="pre">True</span></code>.</p></li>
<li><p><code class="docutils literal notranslate"><span class="pre">None</span></code> - another reserved word. This represents nothing, a null-result or value.</p></li>
<li><p><code class="docutils literal notranslate"><span class="pre">0</span></code> or <code class="docutils literal notranslate"><span class="pre">0.0</span></code></p></li>
<li><p>The empty strings <code class="docutils literal notranslate"><span class="pre">&quot;&quot;</span></code>, <code class="docutils literal notranslate"><span class="pre">''</span></code>, or empty triple-strings like <code class="docutils literal notranslate"><span class="pre">&quot;&quot;&quot;&quot;&quot;&quot;</span></code>, <code class="docutils literal notranslate"><span class="pre">''''''</span></code></p></li>
<li><p>Empty <em>iterables</em> we havent used yet, like empty lists <code class="docutils literal notranslate"><span class="pre">[]</span></code>, empty tuples <code class="docutils literal notranslate"><span class="pre">()</span></code> and empty dicts <code class="docutils literal notranslate"><span class="pre">{}</span></code>.</p></li>
<li><p>Everything else is “truthy”.</p></li>
</ul>
</li>
<li><p><strong>Line 16</strong>s condition is <code class="docutils literal notranslate"><span class="pre">not</span> <span class="pre">args</span></code>. The <code class="docutils literal notranslate"><span class="pre">not</span></code> <em>inverses</em> the result, so if <code class="docutils literal notranslate"><span class="pre">args</span></code> is the empty string (falsy), the whole conditional becomes truthy. Lets continue in the code:</p></li>
<li><p><strong>Lines 16-17</strong>: This code will only run if the <code class="docutils literal notranslate"><span class="pre">if</span></code> statement is truthy, in this case if <code class="docutils literal notranslate"><span class="pre">args</span></code> is the empty string.</p></li>
<li><p><strong>Line 17</strong>: <code class="docutils literal notranslate"><span class="pre">return</span></code> is a reserved Python word that exits <code class="docutils literal notranslate"><span class="pre">func</span></code> immediately.</p></li>
<li><p><strong>Line 18</strong>: We use <code class="docutils literal notranslate"><span class="pre">self.caller.search</span></code> to look for the target in the current location.</p></li>
<li><p><strong>Lines 19-20</strong>: A feature of <code class="docutils literal notranslate"><span class="pre">.search</span></code> is that it will already inform <code class="docutils literal notranslate"><span class="pre">self.caller</span></code> if it couldnt find the target. In that case, <code class="docutils literal notranslate"><span class="pre">target</span></code> will be <code class="docutils literal notranslate"><span class="pre">None</span></code> and we should just directly <code class="docutils literal notranslate"><span class="pre">return</span></code>.</p></li>
<li><p><strong>Lines 21-22</strong>: At this point we have a suitable target and can send our punching strings to each.</p></li>
</ul>
<p>Finally we must also add this to a CmdSet. Lets add it to <code class="docutils literal notranslate"><span class="pre">MyCmdSet</span></code>.</p>
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="c1"># in mygame/commands/mycommands.py</span>
<span class="c1"># ...</span>
<span class="k">class</span> <span class="nc">MyCmdSet</span><span class="p">(</span><span class="n">CmdSet</span><span class="p">):</span>
<span class="k">def</span> <span class="nf">at_cmdset_creation</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="bp">self</span><span class="o">.</span><span class="n">add</span><span class="p">(</span><span class="n">CmdEcho</span><span class="p">)</span>
<span class="bp">self</span><span class="o">.</span><span class="n">add</span><span class="p">(</span><span class="n">CmdHit</span><span class="p">)</span>
</pre></div>
</div>
<aside class="sidebar">
<p class="sidebar-title">Errors in your code</p>
<p>With longer code snippets to try, it gets more and more likely youll
make an error and get a <code class="docutils literal notranslate"><span class="pre">traceback</span></code> when you reload. This will either appear
directly in-game or in your log (view it with <code class="docutils literal notranslate"><span class="pre">evennia</span> <span class="pre">-l</span></code> in a terminal).
Dont panic; tracebacks are your friends - they are to be read bottom-up and usually describe exactly where your problem is. Refer to <a class="reference internal" href="Beginner-Tutorial-Python-basic-introduction.html"><span class="doc std std-doc">The Python introduction lesson</span></a> for more hints. If you get stuck, reach out to the Evennia community for help.</p>
</aside>
<p>Note that since we did <code class="docutils literal notranslate"><span class="pre">py</span> <span class="pre">self.cmdset.remove(&quot;commands.mycommands.MyCmdSet&quot;)</span></code> earlier, this cmdset is no longer available on our Character. Instead we will add these commands directly to our default cmdset.</p>
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="c1"># in mygame/commands/default_cmdsets.py </span>
<span class="c1"># ,.. </span>
<span class="kn">from</span> <span class="nn">.</span> <span class="kn">import</span> <span class="n">mycommands</span>
<span class="k">class</span> <span class="nc">CharacterCmdSet</span><span class="p">(</span><span class="n">default_cmds</span><span class="o">.</span><span class="n">CharacterCmdSet</span><span class="p">):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;</span>
<span class="sd"> The `CharacterCmdSet` contains general in-game commands like `look`,</span>
<span class="sd"> `get`, etc available on in-game Character objects. It is merged with</span>
<span class="sd"> the `AccountCmdSet` when an Account puppets a Character.</span>
<span class="sd"> &quot;&quot;&quot;</span>
<span class="n">key</span> <span class="o">=</span> <span class="s2">&quot;DefaultCharacter&quot;</span>
<span class="k">def</span> <span class="nf">at_cmdset_creation</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;</span>
<span class="sd"> Populates the cmdset</span>
<span class="sd"> &quot;&quot;&quot;</span>
<span class="nb">super</span><span class="p">()</span><span class="o">.</span><span class="n">at_cmdset_creation</span><span class="p">()</span>
<span class="c1">#</span>
<span class="c1"># any commands you add below will overload the default ones.</span>
<span class="c1">#</span>
<span class="bp">self</span><span class="o">.</span><span class="n">add</span><span class="p">(</span><span class="n">mycommands</span><span class="o">.</span><span class="n">MyCmdSet</span><span class="p">)</span> <span class="c1"># &lt;-----------</span>
<span class="c1"># ... </span>
</pre></div>
</div>
<p>We changed from adding the individual <code class="docutils literal notranslate"><span class="pre">echo</span></code> command to adding the entire <code class="docutils literal notranslate"><span class="pre">MyCmdSet</span></code> in one go! This will add all commands in that cmdset to the <code class="docutils literal notranslate"><span class="pre">CharacterCmdSet</span></code> and is a practical way to add a lot of command in one go. Once you explore Evennia further, youll find that <a class="reference internal" href="../../../Contribs/Contribs-Overview.html"><span class="doc std std-doc">Evennia contribs</span></a> all distribute their new commands in cmdsets, so you can easily add them to your game like this.</p>
<p>Next we reload to let Evennia know of these code changes and try it out:</p>
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>&gt; reload
hit
Who do you want to hit?
hit me
You hit YourName with full force!
You got hit by YourName with full force!
</pre></div>
</div>
<p>Lacking a target, we hit ourselves. If you have one of the dragons still around from the previous lesson you could try to hit it (if you dare):</p>
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>hit smaug
You hit Smaug with full force!
</pre></div>
</div>
<p>You wont see the second string. Only Smaug sees that (and is not amused).</p>
</section>
</section>
<section id="summary">
<h2><span class="section-number">8.2. </span>Summary<a class="headerlink" href="#summary" title="Permalink to this headline"></a></h2>
<p>In this lesson we learned how to create our own Command, add it to a CmdSet and then to ourselves. We also upset a dragon.</p>
<p>In the next lesson well learn how to hit Smaug with different weapons. Well also
get into how we replace and extend Evennias default Commands.</p>
</section>
</section>
</div>
</div>
</div>
</div>
<div class="related" role="navigation" aria-label="related navigation">
<h3>Navigation</h3>
<ul>
<li class="right" style="margin-right: 10px">
<a href="../../../genindex.html" title="General Index"
>index</a></li>
<li class="right" >
<a href="../../../py-modindex.html" title="Python Module Index"
>modules</a> |</li>
<li class="right" >
<a href="Beginner-Tutorial-More-on-Commands.html" title="9. Parsing Command input"
>next</a> |</li>
<li class="right" >
<a href="Beginner-Tutorial-Learning-Typeclasses.html" title="7. Making objects persistent"
>previous</a> |</li>
<li class="nav-item nav-item-0"><a href="../../../index.html">Evennia latest</a> &#187;</li>
<li class="nav-item nav-item-1"><a href="../../Howtos-Overview.html" >Tutorials and How-Tos</a> &#187;</li>
<li class="nav-item nav-item-2"><a href="../Beginner-Tutorial-Overview.html" >Beginner Tutorial</a> &#187;</li>
<li class="nav-item nav-item-3"><a href="Beginner-Tutorial-Part1-Overview.html" >Part 1: What We Have</a> &#187;</li>
<li class="nav-item nav-item-this"><a href=""><span class="section-number">8. </span>Adding custom commands</a></li>
</ul>
</div>
<div class="footer" role="contentinfo">
&#169; Copyright 2024, The Evennia developer community.
Created using <a href="https://www.sphinx-doc.org/">Sphinx</a> 3.2.1.
</div>
</body>
</html>