mirror of
https://github.com/evennia/evennia.git
synced 2026-03-18 13:56:30 +01:00
586 lines
No EOL
51 KiB
HTML
586 lines
No EOL
51 KiB
HTML
|
||
<!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>Customize channels — Evennia 0.9.5 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>
|
||
<script async="async" src="https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.7/latest.js?config=TeX-AMS-MML_HTMLorMML"></script>
|
||
<script type="text/x-mathjax-config">MathJax.Hub.Config({"tex2jax": {"processClass": "tex2jax_process|mathjax_process|math|output_area"}})</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" />
|
||
</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="nav-item nav-item-0"><a href="index.html">Evennia 0.9.5</a> »</li>
|
||
<li class="nav-item nav-item-this"><a href="">Customize channels</a></li>
|
||
</ul>
|
||
</div>
|
||
|
||
<div class="document">
|
||
<div class="documentwrapper">
|
||
<div class="bodywrapper">
|
||
<div class="body" role="main">
|
||
|
||
<section class="tex2jax_ignore mathjax_ignore" id="customize-channels">
|
||
<h1>Customize channels<a class="headerlink" href="#customize-channels" title="Permalink to this headline">¶</a></h1>
|
||
</section>
|
||
<section class="tex2jax_ignore mathjax_ignore" id="channel-commands-in-evennia">
|
||
<h1>Channel commands in Evennia<a class="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>
|
||
<section id="what-we-will-try-to-do">
|
||
<h2>What we will try to do<a class="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>
|
||
<ul class="simple">
|
||
<li><p>Remove all the default commands to handle channels.</p></li>
|
||
<li><p>Add a <code class="docutils literal notranslate"><span class="pre">+</span></code> and <code class="docutils literal notranslate"><span class="pre">-</span></code> command to join and leave a channel. So, assuming there is
|
||
a <code class="docutils literal notranslate"><span class="pre">public</span></code> channel on your game (most often the case), you could type <code class="docutils literal notranslate"><span class="pre">+public</span></code>
|
||
to join it and <code class="docutils literal notranslate"><span class="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 <code class="docutils literal notranslate"><span class="pre">cdesc</span> <span class="pre">public</span> <span class="pre">=</span> <span class="pre">My</span> <span class="pre">public</span> <span class="pre">channel</span></code>,
|
||
you would write <code class="docutils literal notranslate"><span class="pre">public/desc</span> <span class="pre">My</span> <span class="pre">public</span> <span class="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
|
||
for a while longer.</p>
|
||
</div></blockquote>
|
||
</section>
|
||
<section id="a-command-to-join-another-to-leave">
|
||
<h2>A command to join, another to leave<a class="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? <code class="docutils literal notranslate"><span class="pre">public/join</span></code> and <code class="docutils literal notranslate"><span class="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 <code class="docutils literal notranslate"><span class="pre">comms.py</span></code> in your <code class="docutils literal notranslate"><span class="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>
|
||
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="c1"># in commands/comms.py</span>
|
||
<span class="kn">from</span> <span class="nn">evennia.utils.search</span> <span class="kn">import</span> <span class="n">search_channel</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">CmdConnect</span><span class="p">(</span><span class="n">Command</span><span class="p">):</span>
|
||
<span class="sd">"""</span>
|
||
<span class="sd"> Connect to a channel.</span>
|
||
<span class="sd"> """</span>
|
||
|
||
<span class="n">key</span> <span class="o">=</span> <span class="s2">"+"</span>
|
||
<span class="n">help_category</span> <span class="o">=</span> <span class="s2">"Comms"</span>
|
||
<span class="n">locks</span> <span class="o">=</span> <span class="s2">"cmd:not pperm(channel_banned)"</span>
|
||
<span class="n">auto_help</span> <span class="o">=</span> <span class="kc">False</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="sd">"""Implement the command"""</span>
|
||
<span class="n">caller</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">caller</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="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">msg</span><span class="p">(</span><span class="s2">"Which channel do you want to connect to?"</span><span class="p">)</span>
|
||
<span class="k">return</span>
|
||
|
||
<span class="n">channelname</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">args</span>
|
||
<span class="n">channel</span> <span class="o">=</span> <span class="n">search_channel</span><span class="p">(</span><span class="n">channelname</span><span class="p">)</span>
|
||
<span class="k">if</span> <span class="ow">not</span> <span class="n">channel</span><span class="p">:</span>
|
||
<span class="k">return</span>
|
||
|
||
<span class="c1"># Check permissions</span>
|
||
<span class="k">if</span> <span class="ow">not</span> <span class="n">channel</span><span class="o">.</span><span class="n">access</span><span class="p">(</span><span class="n">caller</span><span class="p">,</span> <span class="s1">'listen'</span><span class="p">):</span>
|
||
<span class="bp">self</span><span class="o">.</span><span class="n">msg</span><span class="p">(</span><span class="s2">"</span><span class="si">%s</span><span class="s2">: You are not allowed to listen to this channel."</span> <span class="o">%</span> <span class="n">channel</span><span class="o">.</span><span class="n">key</span><span class="p">)</span>
|
||
<span class="k">return</span>
|
||
|
||
<span class="c1"># If not connected to the channel, try to connect</span>
|
||
<span class="k">if</span> <span class="ow">not</span> <span class="n">channel</span><span class="o">.</span><span class="n">has_connection</span><span class="p">(</span><span class="n">caller</span><span class="p">):</span>
|
||
<span class="k">if</span> <span class="ow">not</span> <span class="n">channel</span><span class="o">.</span><span class="n">connect</span><span class="p">(</span><span class="n">caller</span><span class="p">):</span>
|
||
<span class="bp">self</span><span class="o">.</span><span class="n">msg</span><span class="p">(</span><span class="s2">"</span><span class="si">%s</span><span class="s2">: You are not allowed to join this channel."</span> <span class="o">%</span> <span class="n">channel</span><span class="o">.</span><span class="n">key</span><span class="p">)</span>
|
||
<span class="k">return</span>
|
||
<span class="k">else</span><span class="p">:</span>
|
||
<span class="bp">self</span><span class="o">.</span><span class="n">msg</span><span class="p">(</span><span class="s2">"You now are connected to the </span><span class="si">%s</span><span class="s2"> channel. "</span> <span class="o">%</span> <span class="n">channel</span><span class="o">.</span><span class="n">key</span><span class="o">.</span><span class="n">lower</span><span class="p">())</span>
|
||
<span class="k">else</span><span class="p">:</span>
|
||
<span class="bp">self</span><span class="o">.</span><span class="n">msg</span><span class="p">(</span><span class="s2">"You already are connected to the </span><span class="si">%s</span><span class="s2"> channel. "</span> <span class="o">%</span> <span class="n">channel</span><span class="o">.</span><span class="n">key</span><span class="o">.</span><span class="n">lower</span><span class="p">())</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>Okay, let’s review this code, but if you’re used to Evennia commands, it shouldn’t be too strange:</p>
|
||
<ol class="simple">
|
||
<li><p>We import <code class="docutils literal notranslate"><span class="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 <code class="docutils literal notranslate"><span class="pre">evennia.utils.search</span></code>. It’s just more convenient.</p></li>
|
||
<li><p>Our class <code class="docutils literal notranslate"><span class="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 <code class="docutils literal notranslate"><span class="pre">"+"</span></code>. When you enter <code class="docutils literal notranslate"><span class="pre">+something</span></code> in the game, it will
|
||
try to find a command key <code class="docutils literal notranslate"><span class="pre">+something</span></code>. Failing that, it will look at other potential matches.
|
||
Evennia is smart enough to understand that when we type <code class="docutils literal notranslate"><span class="pre">+something</span></code>, <code class="docutils literal notranslate"><span class="pre">+</span></code> is the command key and
|
||
<code class="docutils literal notranslate"><span class="pre">something</span></code> is the command argument. This will, of course, fail if you have a command beginning by
|
||
<code class="docutils literal notranslate"><span class="pre">+</span></code> conflicting with the <code class="docutils literal notranslate"><span class="pre">CmdConnect</span></code> key.</p></li>
|
||
<li><p>We have altered some class attributes, like <code class="docutils literal notranslate"><span class="pre">auto_help</span></code>. If you want to know what they do and
|
||
why they have changed here, you can check the <a class="reference internal" href="Commands.html"><span class="doc std std-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 <code class="docutils literal notranslate"><span class="pre">self.args</span></code>). Following the same example, if a player enters
|
||
<code class="docutils literal notranslate"><span class="pre">+something</span></code>, <code class="docutils literal notranslate"><span class="pre">self.args</span></code> should contain <code class="docutils literal notranslate"><span class="pre">"something"</span></code>. We use <code class="docutils literal notranslate"><span class="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 <code class="docutils literal notranslate"><span class="pre">connect</span></code> method to do this. Pretty straightforward eh?</p></li>
|
||
</ol>
|
||
<p>Now we’ll add a command to leave a channel. It’s almost the same, turned upside down:</p>
|
||
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="k">class</span> <span class="nc">CmdDisconnect</span><span class="p">(</span><span class="n">Command</span><span class="p">):</span>
|
||
<span class="sd">"""</span>
|
||
<span class="sd"> Disconnect from a channel.</span>
|
||
<span class="sd"> """</span>
|
||
|
||
<span class="n">key</span> <span class="o">=</span> <span class="s2">"-"</span>
|
||
<span class="n">help_category</span> <span class="o">=</span> <span class="s2">"Comms"</span>
|
||
<span class="n">locks</span> <span class="o">=</span> <span class="s2">"cmd:not pperm(channel_banned)"</span>
|
||
<span class="n">auto_help</span> <span class="o">=</span> <span class="kc">False</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="sd">"""Implement the command"""</span>
|
||
<span class="n">caller</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">caller</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="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">msg</span><span class="p">(</span><span class="s2">"Which channel do you want to disconnect from?"</span><span class="p">)</span>
|
||
<span class="k">return</span>
|
||
|
||
<span class="n">channelname</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">args</span>
|
||
<span class="n">channel</span> <span class="o">=</span> <span class="n">search_channel</span><span class="p">(</span><span class="n">channelname</span><span class="p">)</span>
|
||
<span class="k">if</span> <span class="ow">not</span> <span class="n">channel</span><span class="p">:</span>
|
||
<span class="k">return</span>
|
||
|
||
<span class="c1"># If connected to the channel, try to disconnect</span>
|
||
<span class="k">if</span> <span class="n">channel</span><span class="o">.</span><span class="n">has_connection</span><span class="p">(</span><span class="n">caller</span><span class="p">):</span>
|
||
<span class="k">if</span> <span class="ow">not</span> <span class="n">channel</span><span class="o">.</span><span class="n">disconnect</span><span class="p">(</span><span class="n">caller</span><span class="p">):</span>
|
||
<span class="bp">self</span><span class="o">.</span><span class="n">msg</span><span class="p">(</span><span class="s2">"</span><span class="si">%s</span><span class="s2">: You are not allowed to disconnect from this channel."</span> <span class="o">%</span> <span class="n">channel</span><span class="o">.</span><span class="n">key</span><span class="p">)</span>
|
||
<span class="k">return</span>
|
||
<span class="k">else</span><span class="p">:</span>
|
||
<span class="bp">self</span><span class="o">.</span><span class="n">msg</span><span class="p">(</span><span class="s2">"You stop listening to the </span><span class="si">%s</span><span class="s2"> channel. "</span> <span class="o">%</span> <span class="n">channel</span><span class="o">.</span><span class="n">key</span><span class="o">.</span><span class="n">lower</span><span class="p">())</span>
|
||
<span class="k">else</span><span class="p">:</span>
|
||
<span class="bp">self</span><span class="o">.</span><span class="n">msg</span><span class="p">(</span><span class="s2">"You are not connected to the </span><span class="si">%s</span><span class="s2"> channel. "</span> <span class="o">%</span> <span class="n">channel</span><span class="o">.</span><span class="n">key</span><span class="o">.</span><span class="n">lower</span><span class="p">())</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>So far, you shouldn’t have trouble following what this command does: it’s
|
||
pretty much the same as the <code class="docutils literal notranslate"><span class="pre">CmdConnect</span></code> class in logic, though it accomplishes
|
||
the opposite. If you are connected to the channel <code class="docutils literal notranslate"><span class="pre">public</span></code> you could
|
||
disconnect from it using <code class="docutils literal notranslate"><span class="pre">-public</span></code>. Remember, you can use channel aliases too
|
||
(<code class="docutils literal notranslate"><span class="pre">+pub</span></code> and <code class="docutils literal notranslate"><span class="pre">-pub</span></code> will also work, assuming you have the alias <code class="docutils literal notranslate"><span class="pre">pub</span></code> on the
|
||
<code class="docutils literal notranslate"><span class="pre">public</span></code> channel).</p>
|
||
<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 <code class="docutils literal notranslate"><span class="pre">AccountCmdSet</span></code> or <code class="docutils literal notranslate"><span class="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 <code class="docutils literal notranslate"><span class="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 <code class="docutils literal notranslate"><span class="pre">AccountCmdSet</span></code>.
|
||
Edit the file <code class="docutils literal notranslate"><span class="pre">commands/default_cmdsets.py</span></code> to change a few things:</p>
|
||
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="c1"># In commands/default_cmdsets.py</span>
|
||
<span class="kn">from</span> <span class="nn">evennia</span> <span class="kn">import</span> <span class="n">default_cmds</span>
|
||
<span class="kn">from</span> <span class="nn">commands.comms</span> <span class="kn">import</span> <span class="n">CmdConnect</span><span class="p">,</span> <span class="n">CmdDisconnect</span>
|
||
|
||
|
||
<span class="c1"># ... Skip to the AccountCmdSet class ...</span>
|
||
|
||
<span class="k">class</span> <span class="nc">AccountCmdSet</span><span class="p">(</span><span class="n">default_cmds</span><span class="o">.</span><span class="n">AccountCmdSet</span><span class="p">):</span>
|
||
<span class="sd">"""</span>
|
||
<span class="sd"> This is the cmdset available to the Account at all times. It is</span>
|
||
<span class="sd"> combined with the `CharacterCmdSet` when the Account puppets a</span>
|
||
<span class="sd"> Character. It holds game-account-specific commands, channel</span>
|
||
<span class="sd"> commands, etc.</span>
|
||
<span class="sd"> """</span>
|
||
<span class="n">key</span> <span class="o">=</span> <span class="s2">"DefaultAccount"</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="sd">"""</span>
|
||
<span class="sd"> Populates the cmdset</span>
|
||
<span class="sd"> """</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"># Channel commands</span>
|
||
<span class="bp">self</span><span class="o">.</span><span class="n">add</span><span class="p">(</span><span class="n">CmdConnect</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">CmdDisconnect</span><span class="p">())</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>Save, reload your game, and you should be able to use <code class="docutils literal notranslate"><span class="pre">+public</span></code> and <code class="docutils literal notranslate"><span class="pre">-public</span></code>
|
||
now!</p>
|
||
</section>
|
||
<section id="a-generic-channel-command-with-switches">
|
||
<h2>A generic channel command with switches<a class="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 <code class="docutils literal notranslate"><span class="pre">public</span> <span class="pre">Hello</span> <span class="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 <code class="docutils literal notranslate"><span class="pre">public</span> <span class="pre">message</span></code> in your game, Evennia calls the <code class="docutils literal notranslate"><span class="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>
|
||
<section id="some-additional-imports">
|
||
<h3>Some additional imports<a class="headerlink" href="#some-additional-imports" title="Permalink to this headline">¶</a></h3>
|
||
<p>You’ll need to add a line of import in your <code class="docutils literal notranslate"><span class="pre">commands/comms.py</span></code> file. We’ll
|
||
see why this import is important when diving in the command itself:</p>
|
||
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="kn">from</span> <span class="nn">evennia.comms.models</span> <span class="kn">import</span> <span class="n">ChannelDB</span>
|
||
</pre></div>
|
||
</div>
|
||
</section>
|
||
<section id="the-class-layout">
|
||
<h3>The class layout<a class="headerlink" href="#the-class-layout" title="Permalink to this headline">¶</a></h3>
|
||
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="c1"># In commands/comms.py</span>
|
||
<span class="k">class</span> <span class="nc">ChannelCommand</span><span class="p">(</span><span class="n">Command</span><span class="p">):</span>
|
||
<span class="sd">"""</span>
|
||
<span class="sd"> {channelkey} channel</span>
|
||
|
||
<span class="sd"> {channeldesc}</span>
|
||
|
||
<span class="sd"> Usage:</span>
|
||
<span class="sd"> {lower_channelkey} <message></span>
|
||
<span class="sd"> {lower_channelkey}/history [start]</span>
|
||
<span class="sd"> {lower_channelkey}/me <message></span>
|
||
<span class="sd"> {lower_channelkey}/who</span>
|
||
|
||
<span class="sd"> Switch:</span>
|
||
<span class="sd"> history: View 20 previous messages, either from the end or</span>
|
||
<span class="sd"> from <start> number of messages from the end.</span>
|
||
<span class="sd"> me: Perform an emote on this channel.</span>
|
||
<span class="sd"> who: View who is connected to this channel.</span>
|
||
|
||
<span class="sd"> Example:</span>
|
||
<span class="sd"> {lower_channelkey} Hello World!</span>
|
||
<span class="sd"> {lower_channelkey}/history</span>
|
||
<span class="sd"> {lower_channelkey}/history 30</span>
|
||
<span class="sd"> {lower_channelkey}/me grins.</span>
|
||
<span class="sd"> {lower_channelkey}/who</span>
|
||
<span class="sd"> """</span>
|
||
<span class="c1"># note that channeldesc and lower_channelkey will be filled</span>
|
||
<span class="c1"># automatically by ChannelHandler</span>
|
||
|
||
<span class="c1"># this flag is what identifies this cmd as a channel cmd</span>
|
||
<span class="c1"># and branches off to the system send-to-channel command</span>
|
||
<span class="c1"># (which is customizable by admin)</span>
|
||
<span class="n">is_channel</span> <span class="o">=</span> <span class="kc">True</span>
|
||
<span class="n">key</span> <span class="o">=</span> <span class="s2">"general"</span>
|
||
<span class="n">help_category</span> <span class="o">=</span> <span class="s2">"Channel Names"</span>
|
||
<span class="n">obj</span> <span class="o">=</span> <span class="kc">None</span>
|
||
<span class="n">arg_regex</span> <span class="o">=</span> <span class="s2">""</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>There are some differences here compared to most common commands.</p>
|
||
<ul class="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. <code class="docutils literal notranslate"><span class="pre">{channelkey}</span></code> will be replaced by the actual channel key (like
|
||
public). <code class="docutils literal notranslate"><span class="pre">{channeldesc}</span></code> will be replaced by the channel description (like
|
||
“public channel”). And <code class="docutils literal notranslate"><span class="pre">{lower_channelkey}</span></code>.</p></li>
|
||
<li><p>We have set <code class="docutils literal notranslate"><span class="pre">is_channel</span></code> to <code class="docutils literal notranslate"><span class="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><code class="docutils literal notranslate"><span class="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 <code class="docutils literal notranslate"><span class="pre">obj</span></code> class variable is another one we won’t detail right now.</p></li>
|
||
<li><p><code class="docutils literal notranslate"><span class="pre">arg_regex</span></code> is important: the default <code class="docutils literal notranslate"><span class="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 <code class="docutils literal notranslate"><span class="pre">key</span></code>
|
||
set to <code class="docutils literal notranslate"><span class="pre">public</span></code> and <code class="docutils literal notranslate"><span class="pre">aliases</span></code> set to the channel aliases (like <code class="docutils literal notranslate"><span class="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>
|
||
</section>
|
||
<section id="the-parse-method">
|
||
<h3>The parse method<a class="headerlink" href="#the-parse-method" title="Permalink to this headline">¶</a></h3>
|
||
<p>The <code class="docutils literal notranslate"><span class="pre">parse</span></code> method is called before <code class="docutils literal notranslate"><span class="pre">func</span></code> in every command. Its job is to
|
||
parse arguments and in our case, we will analyze switches here.</p>
|
||
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="c1"># ...</span>
|
||
<span class="k">def</span> <span class="nf">parse</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
|
||
<span class="sd">"""</span>
|
||
<span class="sd"> Simple parser</span>
|
||
<span class="sd"> """</span>
|
||
<span class="c1"># channel-handler sends channame:msg here.</span>
|
||
<span class="n">channelname</span><span class="p">,</span> <span class="n">msg</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">split</span><span class="p">(</span><span class="s2">":"</span><span class="p">,</span> <span class="mi">1</span><span class="p">)</span>
|
||
<span class="bp">self</span><span class="o">.</span><span class="n">switch</span> <span class="o">=</span> <span class="kc">None</span>
|
||
<span class="k">if</span> <span class="n">msg</span><span class="o">.</span><span class="n">startswith</span><span class="p">(</span><span class="s2">"/"</span><span class="p">):</span>
|
||
<span class="k">try</span><span class="p">:</span>
|
||
<span class="n">switch</span><span class="p">,</span> <span class="n">msg</span> <span class="o">=</span> <span class="n">msg</span><span class="p">[</span><span class="mi">1</span><span class="p">:]</span><span class="o">.</span><span class="n">split</span><span class="p">(</span><span class="s2">" "</span><span class="p">,</span> <span class="mi">1</span><span class="p">)</span>
|
||
<span class="k">except</span> <span class="ne">ValueError</span><span class="p">:</span>
|
||
<span class="n">switch</span> <span class="o">=</span> <span class="n">msg</span><span class="p">[</span><span class="mi">1</span><span class="p">:]</span>
|
||
<span class="n">msg</span> <span class="o">=</span> <span class="s2">""</span>
|
||
|
||
<span class="bp">self</span><span class="o">.</span><span class="n">switch</span> <span class="o">=</span> <span class="n">switch</span><span class="o">.</span><span class="n">lower</span><span class="p">()</span><span class="o">.</span><span class="n">strip</span><span class="p">()</span>
|
||
|
||
<span class="bp">self</span><span class="o">.</span><span class="n">args</span> <span class="o">=</span> <span class="p">(</span><span class="n">channelname</span><span class="o">.</span><span class="n">strip</span><span class="p">(),</span> <span class="n">msg</span><span class="o">.</span><span class="n">strip</span><span class="p">())</span>
|
||
</pre></div>
|
||
</div>
|
||
<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
|
||
<code class="docutils literal notranslate"><span class="pre">args</span></code> will contain <code class="docutils literal notranslate"><span class="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 <code class="docutils literal notranslate"><span class="pre">public/me</span> <span class="pre">jumps</span> <span class="pre">up</span> <span class="pre">and</span> <span class="pre">down</span></code>, for instance. If there is a switch, we save it in <code class="docutils literal notranslate"><span class="pre">self.switch</span></code>. We
|
||
alter <code class="docutils literal notranslate"><span class="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 <code class="docutils literal notranslate"><span class="pre">self.switch</span></code>, not in the second element of <code class="docutils literal notranslate"><span class="pre">self.args</span></code>).</p>
|
||
</section>
|
||
<section id="the-command-func">
|
||
<h3>The command func<a class="headerlink" href="#the-command-func" title="Permalink to this headline">¶</a></h3>
|
||
<p>Finally, let’s see the <code class="docutils literal notranslate"><span class="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>
|
||
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="c1"># ...</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="sd">"""</span>
|
||
<span class="sd"> Create a new message and send it to channel, using</span>
|
||
<span class="sd"> the already formatted input.</span>
|
||
<span class="sd"> """</span>
|
||
<span class="n">channelkey</span><span class="p">,</span> <span class="n">msg</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">args</span>
|
||
<span class="n">caller</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">caller</span>
|
||
<span class="n">channel</span> <span class="o">=</span> <span class="n">ChannelDB</span><span class="o">.</span><span class="n">objects</span><span class="o">.</span><span class="n">get_channel</span><span class="p">(</span><span class="n">channelkey</span><span class="p">)</span>
|
||
|
||
<span class="c1"># Check that the channel exists</span>
|
||
<span class="k">if</span> <span class="ow">not</span> <span class="n">channel</span><span class="p">:</span>
|
||
<span class="bp">self</span><span class="o">.</span><span class="n">msg</span><span class="p">(</span><span class="n">_</span><span class="p">(</span><span class="s2">"Channel '</span><span class="si">%s</span><span class="s2">' not found."</span><span class="p">)</span> <span class="o">%</span> <span class="n">channelkey</span><span class="p">)</span>
|
||
<span class="k">return</span>
|
||
|
||
<span class="c1"># Check that the caller is connected</span>
|
||
<span class="k">if</span> <span class="ow">not</span> <span class="n">channel</span><span class="o">.</span><span class="n">has_connection</span><span class="p">(</span><span class="n">caller</span><span class="p">):</span>
|
||
<span class="n">string</span> <span class="o">=</span> <span class="s2">"You are not connected to channel '</span><span class="si">%s</span><span class="s2">'."</span>
|
||
<span class="bp">self</span><span class="o">.</span><span class="n">msg</span><span class="p">(</span><span class="n">string</span> <span class="o">%</span> <span class="n">channelkey</span><span class="p">)</span>
|
||
<span class="k">return</span>
|
||
|
||
<span class="c1"># Check that the caller has send access</span>
|
||
<span class="k">if</span> <span class="ow">not</span> <span class="n">channel</span><span class="o">.</span><span class="n">access</span><span class="p">(</span><span class="n">caller</span><span class="p">,</span> <span class="s1">'send'</span><span class="p">):</span>
|
||
<span class="n">string</span> <span class="o">=</span> <span class="s2">"You are not permitted to send to channel '</span><span class="si">%s</span><span class="s2">'."</span>
|
||
<span class="bp">self</span><span class="o">.</span><span class="n">msg</span><span class="p">(</span><span class="n">string</span> <span class="o">%</span> <span class="n">channelkey</span><span class="p">)</span>
|
||
<span class="k">return</span>
|
||
|
||
<span class="c1"># Handle the various switches</span>
|
||
<span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">switch</span> <span class="o">==</span> <span class="s2">"me"</span><span class="p">:</span>
|
||
<span class="k">if</span> <span class="ow">not</span> <span class="n">msg</span><span class="p">:</span>
|
||
<span class="bp">self</span><span class="o">.</span><span class="n">msg</span><span class="p">(</span><span class="s2">"What do you want to do on this channel?"</span><span class="p">)</span>
|
||
<span class="k">else</span><span class="p">:</span>
|
||
<span class="n">msg</span> <span class="o">=</span> <span class="s2">"</span><span class="si">{}</span><span class="s2"> </span><span class="si">{}</span><span class="s2">"</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="n">caller</span><span class="o">.</span><span class="n">key</span><span class="p">,</span> <span class="n">msg</span><span class="p">)</span>
|
||
<span class="n">channel</span><span class="o">.</span><span class="n">msg</span><span class="p">(</span><span class="n">msg</span><span class="p">,</span> <span class="n">online</span><span class="o">=</span><span class="kc">True</span><span class="p">)</span>
|
||
<span class="k">elif</span> <span class="bp">self</span><span class="o">.</span><span class="n">switch</span><span class="p">:</span>
|
||
<span class="bp">self</span><span class="o">.</span><span class="n">msg</span><span class="p">(</span><span class="s2">"</span><span class="si">{}</span><span class="s2">: Invalid switch </span><span class="si">{}</span><span class="s2">."</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="n">channel</span><span class="o">.</span><span class="n">key</span><span class="p">,</span> <span class="bp">self</span><span class="o">.</span><span class="n">switch</span><span class="p">))</span>
|
||
<span class="k">elif</span> <span class="ow">not</span> <span class="n">msg</span><span class="p">:</span>
|
||
<span class="bp">self</span><span class="o">.</span><span class="n">msg</span><span class="p">(</span><span class="s2">"Say what?"</span><span class="p">)</span>
|
||
<span class="k">else</span><span class="p">:</span>
|
||
<span class="k">if</span> <span class="n">caller</span> <span class="ow">in</span> <span class="n">channel</span><span class="o">.</span><span class="n">mutelist</span><span class="p">:</span>
|
||
<span class="bp">self</span><span class="o">.</span><span class="n">msg</span><span class="p">(</span><span class="s2">"You currently have </span><span class="si">%s</span><span class="s2"> muted."</span> <span class="o">%</span> <span class="n">channel</span><span class="p">)</span>
|
||
<span class="k">return</span>
|
||
<span class="n">channel</span><span class="o">.</span><span class="n">msg</span><span class="p">(</span><span class="n">msg</span><span class="p">,</span> <span class="n">senders</span><span class="o">=</span><span class="bp">self</span><span class="o">.</span><span class="n">caller</span><span class="p">,</span> <span class="n">online</span><span class="o">=</span><span class="kc">True</span><span class="p">)</span>
|
||
</pre></div>
|
||
</div>
|
||
<ul class="simple">
|
||
<li><p>First of all, we try to get the channel object from the channel name we have
|
||
in the <code class="docutils literal notranslate"><span class="pre">self.args</span></code> tuple. We use <code class="docutils literal notranslate"><span class="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,
|
||
<code class="docutils literal notranslate"><span class="pre">channelname</span></code> in the <code class="docutils literal notranslate"><span class="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 <code class="docutils literal notranslate"><span class="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: <code class="docutils literal notranslate"><span class="pre">me</span></code>. This switch would
|
||
be used if a player entered <code class="docutils literal notranslate"><span class="pre">public/me</span> <span class="pre">jumps</span> <span class="pre">up</span> <span class="pre">and</span> <span class="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>
|
||
</section>
|
||
<section id="end-of-class">
|
||
<h3>End of class<a class="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
|
||
add:</p>
|
||
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="c1"># ...</span>
|
||
<span class="k">def</span> <span class="nf">get_extra_info</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">caller</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">):</span>
|
||
<span class="sd">"""</span>
|
||
<span class="sd"> Let users know that this command is for communicating on a channel.</span>
|
||
|
||
<span class="sd"> Args:</span>
|
||
<span class="sd"> caller (TypedObject): A Character or Account who has entered an ambiguous command.</span>
|
||
|
||
<span class="sd"> Returns:</span>
|
||
<span class="sd"> A string with identifying information to disambiguate the object, conventionally with a</span>
|
||
<span class="sd">preceding space.</span>
|
||
<span class="sd"> """</span>
|
||
<span class="k">return</span> <span class="s2">" (channel)"</span>
|
||
</pre></div>
|
||
</div>
|
||
</section>
|
||
<section id="adding-this-channel-command">
|
||
<h3>Adding this channel command<a class="headerlink" href="#adding-this-channel-command" title="Permalink to this headline">¶</a></h3>
|
||
<p>Contrary to most Evennia commands, we won’t add our <code class="docutils literal notranslate"><span class="pre">ChannelCommand</span></code> to a
|
||
<code class="docutils literal notranslate"><span class="pre">CmdSet</span></code>. Instead we need to tell Evennia that it should use the command we
|
||
just created instead of its default channel-command.</p>
|
||
<p>In your <code class="docutils literal notranslate"><span class="pre">server/conf/settings.py</span></code> file, add a new setting:</p>
|
||
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="c1"># Channel options</span>
|
||
<span class="n">CHANNEL_COMMAND_CLASS</span> <span class="o">=</span> <span class="s2">"commands.comms.ChannelCommand"</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>Then you can reload your game. Try to type <code class="docutils literal notranslate"><span class="pre">public</span> <span class="pre">hello</span></code> and <code class="docutils literal notranslate"><span class="pre">public/me</span> <span class="pre">jumps</span> <span class="pre">up</span> <span class="pre">and</span> <span class="pre">down</span></code>. Don’t forget to enter <code class="docutils literal notranslate"><span class="pre">help</span> <span class="pre">public</span></code> to see if your command has
|
||
truly been added.</p>
|
||
</section>
|
||
</section>
|
||
<section id="conclusion-and-full-code">
|
||
<h2>Conclusion and full code<a class="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>
|
||
<ul class="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
|
||
this article.</p>
|
||
<p><a class="reference external" href="https://github.com/vincent-lg/avenew/blob/master/commands/comms.py">Read the full example on Github</a></p>
|
||
</section>
|
||
</section>
|
||
|
||
|
||
<div class="clearer"></div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<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>
|
||
<p><h3><a href="index.html">Table of Contents</a></h3>
|
||
<ul>
|
||
<li><a class="reference internal" href="#">Customize channels</a></li>
|
||
<li><a class="reference internal" href="#channel-commands-in-evennia">Channel commands in Evennia</a><ul>
|
||
<li><a class="reference internal" href="#what-we-will-try-to-do">What we will try to do</a></li>
|
||
<li><a class="reference internal" href="#a-command-to-join-another-to-leave">A command to join, another to leave</a></li>
|
||
<li><a class="reference internal" href="#a-generic-channel-command-with-switches">A generic channel command with switches</a><ul>
|
||
<li><a class="reference internal" href="#some-additional-imports">Some additional imports</a></li>
|
||
<li><a class="reference internal" href="#the-class-layout">The class layout</a></li>
|
||
<li><a class="reference internal" href="#the-parse-method">The parse method</a></li>
|
||
<li><a class="reference internal" href="#the-command-func">The command func</a></li>
|
||
<li><a class="reference internal" href="#end-of-class">End of class</a></li>
|
||
<li><a class="reference internal" href="#adding-this-channel-command">Adding this channel command</a></li>
|
||
</ul>
|
||
</li>
|
||
<li><a class="reference internal" href="#conclusion-and-full-code">Conclusion and full code</a></li>
|
||
</ul>
|
||
</li>
|
||
</ul>
|
||
|
||
<div role="note" aria-label="source link">
|
||
<!--h3>This Page</h3-->
|
||
<ul class="this-page-menu">
|
||
<li><a href="_sources/Customize-channels.md.txt"
|
||
rel="nofollow">Show Page Source</a></li>
|
||
</ul>
|
||
</div><h3>Links</h3>
|
||
<ul>
|
||
<li><a href="https://www.evennia.com">Home page</a> </li>
|
||
<li><a href="https://github.com/evennia/evennia">Evennia Github</a> </li>
|
||
<li><a href="http://games.evennia.com">Game Index</a> </li>
|
||
<li><a href="http://webchat.freenode.net/?channels=evennia&uio=MT1mYWxzZSY5PXRydWUmMTE9MTk1JjEyPXRydWUbb">IRC</a> -
|
||
<a href="https://discord.gg/NecFePw">Discord</a> -
|
||
<a href="https://groups.google.com/forum/#%21forum/evennia">Forums</a>
|
||
</li>
|
||
<li><a href="http://evennia.blogspot.com/">Evennia Dev blog</a> </li>
|
||
</ul>
|
||
<h3>Versions</h3>
|
||
<ul>
|
||
<li><a href="../1.0-dev/index.html">1.0-dev (develop branch)</a></li>
|
||
<li><a href="Customize-channels.html">0.9.5 (v0.9.5 branch)</a></li>
|
||
</ul>
|
||
|
||
|
||
</div>
|
||
</div>
|
||
<div class="clearer"></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="nav-item nav-item-0"><a href="index.html">Evennia 0.9.5</a> »</li>
|
||
<li class="nav-item nav-item-this"><a href="">Customize channels</a></li>
|
||
</ul>
|
||
</div>
|
||
<div class="footer" role="contentinfo">
|
||
© Copyright 2020, The Evennia developer community.
|
||
Created using <a href="https://www.sphinx-doc.org/">Sphinx</a> 3.2.1.
|
||
</div>
|
||
</body>
|
||
</html> |