<h1>Channel commands in Evennia<aclass="headerlink"href="#channel-commands-in-evennia"title="Permalink to this headline">¶</a></h1>
<p>By default, Evennia’s default channel commands are inspired by MUX. They all
begin with “c” followed by the action to perform (like “ccreate” or “cdesc”).
If this default seems strange to you compared to other Evennia commands that
rely on switches, you might want to check this tutorial out.</p>
<p>This tutorial will also give you insight into the workings of the channel system.
So it may be useful even if you don’t plan to make the exact changes shown here.</p>
<divclass="section"id="what-we-will-try-to-do">
<h2>What we will try to do<aclass="headerlink"href="#what-we-will-try-to-do"title="Permalink to this headline">¶</a></h2>
<p>Our mission: change the default channel commands to have a different syntax.</p>
<p>This tutorial will do the following changes:</p>
<ulclass="simple">
<li><p>Remove all the default commands to handle channels.</p></li>
<li><p>Add a <codeclass="docutils literal notranslate"><spanclass="pre">+</span></code> and <codeclass="docutils literal notranslate"><spanclass="pre">-</span></code> command to join and leave a channel. So, assuming there is
a <codeclass="docutils literal notranslate"><spanclass="pre">public</span></code> channel on your game (most often the case), you could type <codeclass="docutils literal notranslate"><spanclass="pre">+public</span></code>
to join it and <codeclass="docutils literal notranslate"><spanclass="pre">-public</span></code> to leave it.</p></li>
<li><p>Group the commands to manipulate channels under the channel name, after a
switch. For instance, instead of writing <codeclass="docutils literal notranslate"><spanclass="pre">cdesc</span><spanclass="pre">public</span><spanclass="pre">=</span><spanclass="pre">My</span><spanclass="pre">public</span><spanclass="pre">channel</span></code>,
you would write <codeclass="docutils literal notranslate"><spanclass="pre">public/desc</span><spanclass="pre">My</span><spanclass="pre">public</span><spanclass="pre">channel</span></code>.</p></li>
</ul>
<blockquote>
<div><p>I listed removing the default Evennia commands as a first step in the
process. Actually, we’ll move it at the very bottom of the list, since we
still want to use them, we might get it wrong and rely on Evennia commands
<h2>A command to join, another to leave<aclass="headerlink"href="#a-command-to-join-another-to-leave"title="Permalink to this headline">¶</a></h2>
<p>We’ll do the most simple task at first: create two commands, one to join a
channel, one to leave.</p>
<blockquote>
<div><p>Why not have them as switches? <codeclass="docutils literal notranslate"><spanclass="pre">public/join</span></code> and <codeclass="docutils literal notranslate"><spanclass="pre">public/leave</span></code> for instance?</p>
</div></blockquote>
<p>For security reasons, I will hide channels to which the caller is not
connected. It means that if the caller is not connected to the “public”
channel, he won’t be able to use the “public” command. This is somewhat
standard: if we create an administrator-only channel, we don’t want players to
try (or even know) the channel command. Again, you could design it a different
way should you want to.</p>
<p>First create a file named <codeclass="docutils literal notranslate"><spanclass="pre">comms.py</span></code> in your <codeclass="docutils literal notranslate"><spanclass="pre">commands</span></code> package. It’s
a rather logical place, since we’ll write different commands to handle
communication.</p>
<p>Okay, let’s add the first command to join a channel:</p>
<spanclass="bp">self</span><spanclass="o">.</span><spanclass="n">msg</span><spanclass="p">(</span><spanclass="s2">"Which channel do you want to connect to?"</span><spanclass="p">)</span>
<spanclass="bp">self</span><spanclass="o">.</span><spanclass="n">msg</span><spanclass="p">(</span><spanclass="s2">"</span><spanclass="si">%s</span><spanclass="s2">: You are not allowed to listen to this channel."</span><spanclass="o">%</span><spanclass="n">channel</span><spanclass="o">.</span><spanclass="n">key</span><spanclass="p">)</span>
<spanclass="k">return</span>
<spanclass="c1"># If not connected to the channel, try to connect</span>
<spanclass="bp">self</span><spanclass="o">.</span><spanclass="n">msg</span><spanclass="p">(</span><spanclass="s2">"</span><spanclass="si">%s</span><spanclass="s2">: You are not allowed to join this channel."</span><spanclass="o">%</span><spanclass="n">channel</span><spanclass="o">.</span><spanclass="n">key</span><spanclass="p">)</span>
<spanclass="k">return</span>
<spanclass="k">else</span><spanclass="p">:</span>
<spanclass="bp">self</span><spanclass="o">.</span><spanclass="n">msg</span><spanclass="p">(</span><spanclass="s2">"You now are connected to the </span><spanclass="si">%s</span><spanclass="s2"> channel. "</span><spanclass="o">%</span><spanclass="n">channel</span><spanclass="o">.</span><spanclass="n">key</span><spanclass="o">.</span><spanclass="n">lower</span><spanclass="p">())</span>
<spanclass="k">else</span><spanclass="p">:</span>
<spanclass="bp">self</span><spanclass="o">.</span><spanclass="n">msg</span><spanclass="p">(</span><spanclass="s2">"You already are connected to the </span><spanclass="si">%s</span><spanclass="s2"> channel. "</span><spanclass="o">%</span><spanclass="n">channel</span><spanclass="o">.</span><spanclass="n">key</span><spanclass="o">.</span><spanclass="n">lower</span><spanclass="p">())</span>
</pre></div>
</td></tr></table></div>
<p>Okay, let’s review this code, but if you’re used to Evennia commands, it shouldn’t be too strange:</p>
<li><p>We import <codeclass="docutils literal notranslate"><spanclass="pre">search_channel</span></code>. This is a little helper function that we will use to search for
channels by name and aliases, found in <codeclass="docutils literal notranslate"><spanclass="pre">evennia.utils.search</span></code>. It’s just more convenient.</p></li>
<li><p>Our class <codeclass="docutils literal notranslate"><spanclass="pre">CmdConnect</span></code> contains the body of our command to join a channel.</p></li>
<li><p>Notice the key of this command is simply <codeclass="docutils literal notranslate"><spanclass="pre">"+"</span></code>. When you enter <codeclass="docutils literal notranslate"><spanclass="pre">+something</span></code> in the game, it will
try to find a command key <codeclass="docutils literal notranslate"><spanclass="pre">+something</span></code>. Failing that, it will look at other potential matches.
Evennia is smart enough to understand that when we type <codeclass="docutils literal notranslate"><spanclass="pre">+something</span></code>, <codeclass="docutils literal notranslate"><spanclass="pre">+</span></code> is the command key and
<codeclass="docutils literal notranslate"><spanclass="pre">something</span></code> is the command argument. This will, of course, fail if you have a command beginning by
<codeclass="docutils literal notranslate"><spanclass="pre">+</span></code> conflicting with the <codeclass="docutils literal notranslate"><spanclass="pre">CmdConnect</span></code> key.</p></li>
<li><p>We have altered some class attributes, like <codeclass="docutils literal notranslate"><spanclass="pre">auto_help</span></code>. If you want to know what they do and
why they have changed here, you can check the <aclass="reference internal"href="../Components/Commands.html"><spanclass="doc">documentation on commands</span></a>.</p></li>
<li><p>In the command body, we begin by extracting the channel name. Remember that this name should be
in the command arguments (that is, in <codeclass="docutils literal notranslate"><spanclass="pre">self.args</span></code>). Following the same example, if a player enters
<codeclass="docutils literal notranslate"><spanclass="pre">+something</span></code>, <codeclass="docutils literal notranslate"><spanclass="pre">self.args</span></code> should contain <codeclass="docutils literal notranslate"><spanclass="pre">"something"</span></code>. We use <codeclass="docutils literal notranslate"><spanclass="pre">search_channel</span></code> to see if this
channel exists.</p></li>
<li><p>We then check the access level of the channel, to see if the caller can listen to it (not
necessarily use it to speak, mind you, just listen to others speak, as these are two different locks
on Evennia).</p></li>
<li><p>Finally, we connect the caller if he’s not already connected to the channel. We use the
channel’s <codeclass="docutils literal notranslate"><spanclass="pre">connect</span></code> method to do this. Pretty straightforward eh?</p></li>
<spanclass="bp">self</span><spanclass="o">.</span><spanclass="n">msg</span><spanclass="p">(</span><spanclass="s2">"Which channel do you want to disconnect from?"</span><spanclass="p">)</span>
<spanclass="bp">self</span><spanclass="o">.</span><spanclass="n">msg</span><spanclass="p">(</span><spanclass="s2">"</span><spanclass="si">%s</span><spanclass="s2">: You are not allowed to disconnect from this channel."</span><spanclass="o">%</span><spanclass="n">channel</span><spanclass="o">.</span><spanclass="n">key</span><spanclass="p">)</span>
<spanclass="k">return</span>
<spanclass="k">else</span><spanclass="p">:</span>
<spanclass="bp">self</span><spanclass="o">.</span><spanclass="n">msg</span><spanclass="p">(</span><spanclass="s2">"You stop listening to the </span><spanclass="si">%s</span><spanclass="s2"> channel. "</span><spanclass="o">%</span><spanclass="n">channel</span><spanclass="o">.</span><spanclass="n">key</span><spanclass="o">.</span><spanclass="n">lower</span><spanclass="p">())</span>
<spanclass="k">else</span><spanclass="p">:</span>
<spanclass="bp">self</span><spanclass="o">.</span><spanclass="n">msg</span><spanclass="p">(</span><spanclass="s2">"You are not connected to the </span><spanclass="si">%s</span><spanclass="s2"> channel. "</span><spanclass="o">%</span><spanclass="n">channel</span><spanclass="o">.</span><spanclass="n">key</span><spanclass="o">.</span><spanclass="n">lower</span><spanclass="p">())</span>
</pre></div>
</td></tr></table></div>
<p>So far, you shouldn’t have trouble following what this command does: it’s
pretty much the same as the <codeclass="docutils literal notranslate"><spanclass="pre">CmdConnect</span></code> class in logic, though it accomplishes
the opposite. If you are connected to the channel <codeclass="docutils literal notranslate"><spanclass="pre">public</span></code> you could
disconnect from it using <codeclass="docutils literal notranslate"><spanclass="pre">-public</span></code>. Remember, you can use channel aliases too
(<codeclass="docutils literal notranslate"><spanclass="pre">+pub</span></code> and <codeclass="docutils literal notranslate"><spanclass="pre">-pub</span></code> will also work, assuming you have the alias <codeclass="docutils literal notranslate"><spanclass="pre">pub</span></code> on the
<p>It’s time to test this code, and to do so, you will need to add these two
commands. Here is a good time to say it: by default, Evennia connects accounts
to channels. Some other games (usually with a higher multisession mode) will
want to connect characters instead of accounts, so that several characters in
the same account can be connected to various channels. You can definitely add
these commands either in the <codeclass="docutils literal notranslate"><spanclass="pre">AccountCmdSet</span></code> or <codeclass="docutils literal notranslate"><spanclass="pre">CharacterCmdSet</span></code>, the caller
will be different and the command will add or remove accounts of characters.
If you decide to install these commands on the <codeclass="docutils literal notranslate"><spanclass="pre">CharacterCmdSet</span></code>, you might
have to disconnect your superuser account (account #1) from the channel before
joining it with your characters, as Evennia tends to subscribe all accounts
automatically if you don’t tell it otherwise.</p>
<p>So here’s an example of how to add these commands into your <codeclass="docutils literal notranslate"><spanclass="pre">AccountCmdSet</span></code>.
Edit the file <codeclass="docutils literal notranslate"><spanclass="pre">commands/default_cmdsets.py</span></code> to change a few things:</p>
<p>Save, reload your game, and you should be able to use <codeclass="docutils literal notranslate"><spanclass="pre">+public</span></code> and <codeclass="docutils literal notranslate"><spanclass="pre">-public</span></code>
<h2>A generic channel command with switches<aclass="headerlink"href="#a-generic-channel-command-with-switches"title="Permalink to this headline">¶</a></h2>
<p>It’s time to dive a little deeper into channel processing. What happens in
Evennia when a player enters <codeclass="docutils literal notranslate"><spanclass="pre">public</span><spanclass="pre">Hello</span><spanclass="pre">everybody!</span></code>?</p>
<p>Like exits, channels are a particular command that Evennia automatically
creates and attaches to individual channels. So when you enter <codeclass="docutils literal notranslate"><spanclass="pre">public</span><spanclass="pre">message</span></code> in your game, Evennia calls the <codeclass="docutils literal notranslate"><spanclass="pre">public</span></code> command.</p>
<blockquote>
<div><p>But I didn’t add any public command…</p>
</div></blockquote>
<p>Evennia will just create these commands automatically based on the existing
channels. The base command is the command we’ll need to edit.</p>
<blockquote>
<div><p>Why edit it? It works just fine to talk.</p>
</div></blockquote>
<p>Unfortunately, if we want to add switches to our channel names, we’ll have to
edit this command. It’s not too hard, however, we’ll just start writing a
standard command with minor twitches.</p>
<divclass="section"id="some-additional-imports">
<h3>Some additional imports<aclass="headerlink"href="#some-additional-imports"title="Permalink to this headline">¶</a></h3>
<p>You’ll need to add a line of import in your <codeclass="docutils literal notranslate"><spanclass="pre">commands/comms.py</span></code> file. We’ll
see why this import is important when diving in the command itself:</p>
<p>There are some differences here compared to most common commands.</p>
<ulclass="simple">
<li><p>There is something disconcerting in the class docstring. Some information is
between curly braces. This is a format-style which is only used for channel
commands. <codeclass="docutils literal notranslate"><spanclass="pre">{channelkey}</span></code> will be replaced by the actual channel key (like
public). <codeclass="docutils literal notranslate"><spanclass="pre">{channeldesc}</span></code> will be replaced by the channel description (like
“public channel”). And <codeclass="docutils literal notranslate"><spanclass="pre">{lower_channelkey}</span></code>.</p></li>
<li><p>We have set <codeclass="docutils literal notranslate"><spanclass="pre">is_channel</span></code> to <codeclass="docutils literal notranslate"><spanclass="pre">True</span></code> in the command class variables. You
shouldn’t worry too much about that: it just tells Evennia this is a special
command just for channels.</p></li>
<li><p><codeclass="docutils literal notranslate"><spanclass="pre">key</span></code> is a bit misleading because it will be replaced eventually. So we
could set it to virtually anything.</p></li>
<li><p>The <codeclass="docutils literal notranslate"><spanclass="pre">obj</span></code> class variable is another one we won’t detail right now.</p></li>
<li><p><codeclass="docutils literal notranslate"><spanclass="pre">arg_regex</span></code> is important: the default <codeclass="docutils literal notranslate"><spanclass="pre">arg_regex</span></code> in the channel command will
forbid to use switches (a slash just after the channel name is not allowed).
That’s why we enforce it here, we allow any syntax.</p></li>
</ul>
<blockquote>
<div><p>What will become of this command?</p>
</div></blockquote>
<p>Well, when we’ll be through with it, and once we’ll add it as the default
command to handle channels, Evennia will create one per existing channel. For
instance, the public channel will receive one command of this class, with <codeclass="docutils literal notranslate"><spanclass="pre">key</span></code>
set to <codeclass="docutils literal notranslate"><spanclass="pre">public</span></code> and <codeclass="docutils literal notranslate"><spanclass="pre">aliases</span></code> set to the channel aliases (like <codeclass="docutils literal notranslate"><spanclass="pre">['pub']</span></code>).</p>
<blockquote>
<div><p>Can I see it work?</p>
</div></blockquote>
<p>Not just yet, there’s still a lot of code needed.</p>
<p>Okay we have the command structure but it’s rather empty.</p>
</div>
<divclass="section"id="the-parse-method">
<h3>The parse method<aclass="headerlink"href="#the-parse-method"title="Permalink to this headline">¶</a></h3>
<p>The <codeclass="docutils literal notranslate"><spanclass="pre">parse</span></code> method is called before <codeclass="docutils literal notranslate"><spanclass="pre">func</span></code> in every command. Its job is to
parse arguments and in our case, we will analyze switches here.</p>
<p>Reading the comments we see that the channel handler will send the command in a
strange way: a string with the channel name, a colon and the actual message
entered by the player. So if the player enters “public hello”, the command
<codeclass="docutils literal notranslate"><spanclass="pre">args</span></code> will contain <codeclass="docutils literal notranslate"><spanclass="pre">"public:hello"</span></code>. You can look at the way the channel name
and message are parsed, this can be used in a lot of different commands.</p>
<p>Next we check if there’s any switch, that is, if the message starts with a
slash. This would be the case if a player entered <codeclass="docutils literal notranslate"><spanclass="pre">public/me</span><spanclass="pre">jumps</span><spanclass="pre">up</span><spanclass="pre">and</span><spanclass="pre">down</span></code>, for instance. If there is a switch, we save it in <codeclass="docutils literal notranslate"><spanclass="pre">self.switch</span></code>. We
alter <codeclass="docutils literal notranslate"><spanclass="pre">self.args</span></code> at the end to contain a tuple with two values: the channel
name, and the message (if a switch was used, notice that the switch will be
stored in <codeclass="docutils literal notranslate"><spanclass="pre">self.switch</span></code>, not in the second element of <codeclass="docutils literal notranslate"><spanclass="pre">self.args</span></code>).</p>
</div>
<divclass="section"id="the-command-func">
<h3>The command func<aclass="headerlink"href="#the-command-func"title="Permalink to this headline">¶</a></h3>
<p>Finally, let’s see the <codeclass="docutils literal notranslate"><spanclass="pre">func</span></code> method in the command class. It will have to
handle switches and also the raw message to send if no switch was used.</p>
<spanclass="bp">self</span><spanclass="o">.</span><spanclass="n">msg</span><spanclass="p">(</span><spanclass="n">_</span><spanclass="p">(</span><spanclass="s2">"Channel '</span><spanclass="si">%s</span><spanclass="s2">' not found."</span><spanclass="p">)</span><spanclass="o">%</span><spanclass="n">channelkey</span><spanclass="p">)</span>
<spanclass="k">return</span>
<spanclass="c1"># Check that the caller is connected</span>
<spanclass="n">string</span><spanclass="o">=</span><spanclass="s2">"You are not connected to channel '</span><spanclass="si">%s</span><spanclass="s2">'."</span>
<spanclass="n">string</span><spanclass="o">=</span><spanclass="s2">"You are not permitted to send to channel '</span><spanclass="si">%s</span><spanclass="s2">'."</span>
<spanclass="bp">self</span><spanclass="o">.</span><spanclass="n">msg</span><spanclass="p">(</span><spanclass="s2">"What do you want to do on this channel?"</span><spanclass="p">)</span>
<spanclass="bp">self</span><spanclass="o">.</span><spanclass="n">msg</span><spanclass="p">(</span><spanclass="s2">"You currently have </span><spanclass="si">%s</span><spanclass="s2"> muted."</span><spanclass="o">%</span><spanclass="n">channel</span><spanclass="p">)</span>
<li><p>First of all, we try to get the channel object from the channel name we have
in the <codeclass="docutils literal notranslate"><spanclass="pre">self.args</span></code> tuple. We use <codeclass="docutils literal notranslate"><spanclass="pre">ChannelDB.objects.get_channel</span></code> this time
because we know the channel name isn’t an alias (that was part of the deal,
<codeclass="docutils literal notranslate"><spanclass="pre">channelname</span></code> in the <codeclass="docutils literal notranslate"><spanclass="pre">parse</span></code> method contains a command key).</p></li>
<li><p>We check that the channel does exist.</p></li>
<li><p>We then check that the caller is connected to the channel. Remember, if the
caller isn’t connected, we shouldn’t allow him to use this command (that
includes the switches on channels).</p></li>
<li><p>We then check that the caller has access to the channel’s <codeclass="docutils literal notranslate"><spanclass="pre">send</span></code> lock. This
time, we make sure the caller can send messages to the channel, no matter what
operation he’s trying to perform.</p></li>
<li><p>Finally we handle switches. We try only one switch: <codeclass="docutils literal notranslate"><spanclass="pre">me</span></code>. This switch would
be used if a player entered <codeclass="docutils literal notranslate"><spanclass="pre">public/me</span><spanclass="pre">jumps</span><spanclass="pre">up</span><spanclass="pre">and</span><spanclass="pre">down</span></code> (to do a channel
emote).</p></li>
<li><p>We handle the case where the switch is unknown and where there’s no switch
(the player simply wants to talk on this channel).</p></li>
</ul>
<p>The good news: The code is not too complicated by itself. The bad news is that
this is just an abridged version of the code. If you want to handle all the
switches mentioned in the command help, you will have more code to write. This
is left as an exercise.</p>
</div>
<divclass="section"id="end-of-class">
<h3>End of class<aclass="headerlink"href="#end-of-class"title="Permalink to this headline">¶</a></h3>
<p>It’s almost done, but we need to add a method in this command class that isn’t
often used. I won’t detail it’s usage too much, just know that Evennia will use
it and will get angry if you don’t add it. So at the end of your class, just
<p>Then you can reload your game. Try to type <codeclass="docutils literal notranslate"><spanclass="pre">public</span><spanclass="pre">hello</span></code> and <codeclass="docutils literal notranslate"><spanclass="pre">public/me</span><spanclass="pre">jumps</span><spanclass="pre">up</span><spanclass="pre">and</span><spanclass="pre">down</span></code>. Don’t forget to enter <codeclass="docutils literal notranslate"><spanclass="pre">help</span><spanclass="pre">public</span></code> to see if your command has
truly been added.</p>
</div>
</div>
<divclass="section"id="conclusion-and-full-code">
<h2>Conclusion and full code<aclass="headerlink"href="#conclusion-and-full-code"title="Permalink to this headline">¶</a></h2>
<p>That was some adventure! And there’s still things to do! But hopefully, this
tutorial will have helped you in designing your own channel system. Here are a
few things to do:</p>
<ulclass="simple">
<li><p>Add more switches to handle various actions, like changing the description of
a channel for instance, or listing the connected participants.</p></li>
<li><p>Remove the default Evennia commands to handle channels.</p></li>
<li><p>Alter the behavior of the channel system so it better aligns with what you
want to do.</p></li>
</ul>
<p>As a special bonus, you can find a full, working example of a communication
system similar to the one I’ve shown you: this is a working example, it
integrates all switches and does ever some extra checking, but it’s also very
close from the code I’ve provided here. Notice, however, that this resource is
external to Evennia and not maintained by anyone but the original author of