<pclass="last">You are reading an old version of the Evennia documentation. <ahref="https://www.evennia.com/docs/latest/index.html">The latest version is here</a></p>.
<h1><spanclass="section-number">1. </span>Code structure and Utilities<aclass="headerlink"href="#code-structure-and-utilities"title="Permalink to this headline">¶</a></h1>
<p>In this lesson we will set up the file structure for <em>EvAdventure</em>. We will make some
utilities that will be useful later. We will also learn how to write <em>tests</em>.</p>
<sectionid="folder-structure">
<h2><spanclass="section-number">1.1. </span>Folder structure<aclass="headerlink"href="#folder-structure"title="Permalink to this headline">¶</a></h2>
<asideclass="sidebar">
<pclass="sidebar-title">This layout is for the tutorial!</p>
<p>We make the <codeclass="docutils literal notranslate"><spanclass="pre">evadventure</span></code> folder stand-alone for the sake of the tutorial only. Leaving the code isolated makes it clear what we changed - and for you to grab what you want later. It also makes it easier to refer to matching code in <codeclass="docutils literal notranslate"><spanclass="pre">evennia/contrib/tutorials/evadventure</span></code>.</p>
<p>For your own game you are encouraged to modify your game dir in-place instead (such as add to <codeclass="docutils literal notranslate"><spanclass="pre">commands/commands.py</span></code> and to modify the <codeclass="docutils literal notranslate"><spanclass="pre">typeclasses/</span></code> modules directly). Except for the <codeclass="docutils literal notranslate"><spanclass="pre">server/</span></code> folder, you are infact free to structure your game dir code pretty much as you like.</p>
</aside>
<p>Create a new folder under your <codeclass="docutils literal notranslate"><spanclass="pre">mygame</span></code> folder, named <codeclass="docutils literal notranslate"><spanclass="pre">evadventure</span></code>. Inside it, create
another folder <codeclass="docutils literal notranslate"><spanclass="pre">tests/</span></code> and make sure to put empty <codeclass="docutils literal notranslate"><spanclass="pre">__init__.py</span></code> files in both. This turns both
folders into packages Python understands to import from.</p>
<p>Importing anything from inside this folder from anywhere else under <codeclass="docutils literal notranslate"><spanclass="pre">mygame</span></code> will be done by</p>
<divclass="highlight-python notranslate"><divclass="highlight"><pre><span></span><spanclass="c1"># from anywhere in mygame/</span>
<p>This is the ‘absolute path` type of import.</p>
<p>Between two modules both in <codeclass="docutils literal notranslate"><spanclass="pre">evadventure/</span></code>, you can use a ‘relative’ import with <codeclass="docutils literal notranslate"><spanclass="pre">.</span></code>:</p>
<divclass="highlight-python notranslate"><divclass="highlight"><pre><span></span><spanclass="c1"># from a module inside mygame/evadventure</span>
<p>From e.g. inside <codeclass="docutils literal notranslate"><spanclass="pre">mygame/evadventure/tests/</span></code> you can import from one level above using <codeclass="docutils literal notranslate"><spanclass="pre">..</span></code>:</p>
<divclass="highlight-python notranslate"><divclass="highlight"><pre><span></span><spanclass="c1"># from mygame/evadventure/tests/ </span>
<p>Create a new file <codeclass="docutils literal notranslate"><spanclass="pre">mygame/evadventure/enums.py</span></code>.</p>
<p>An <aclass="reference external"href="https://docs.python.org/3/library/enum.html">enum</a> (enumeration) is a way to establish constants in Python. Best is to show an example:</p>
<divclass="highlight-python notranslate"><divclass="highlight"><pre><span></span><spanclass="c1"># in a file mygame/evadventure/enums.py</span>
<spanclass="n">Ability</span><spanclass="o">.</span><spanclass="n">STR</span><spanclass="c1"># the enum itself </span>
<spanclass="n">Ability</span><spanclass="o">.</span><spanclass="n">STR</span><spanclass="o">.</span><spanclass="n">value</span><spanclass="c1"># this is the string "strength"</span>
</pre></div>
</div>
<p>Having enums is recommended practice. With them set up, it means we can make sure to refer to the same thing every time. Having all enums in one place also means you have a good overview of the constants you are dealing with.</p>
<p>The alternative would be to for example pass around a string <codeclass="docutils literal notranslate"><spanclass="pre">"constitution"</span></code>. If you mis-spell this (<codeclass="docutils literal notranslate"><spanclass="pre">"consitution"</span></code>), you would not necessarily know it right away - the error would happen later when the string is not recognized. If you make a typo getting <codeclass="docutils literal notranslate"><spanclass="pre">Ability.COM</span></code> instead of <codeclass="docutils literal notranslate"><spanclass="pre">Ability.CON</span></code>, Python will immediately raise an error since this enum is not recognized.</p>
<p>With enums you can also do nice direct comparisons like <codeclass="docutils literal notranslate"><spanclass="pre">if</span><spanclass="pre">ability</span><spanclass="pre">is</span><spanclass="pre">Ability.WIS:</span><spanclass="pre"><do</span><spanclass="pre">stuff></span></code>.</p>
<p>Note that the <codeclass="docutils literal notranslate"><spanclass="pre">Ability.STR</span></code> enum does not have the actual <em>value</em> of e.g. your Strength. It’s just a fixed label for the Strength ability.</p>
<p>Here is the <codeclass="docutils literal notranslate"><spanclass="pre">enum.py</span></code> module needed for <em>Knave</em>. It covers the basic aspects of rule systems we need to track (check out the <em>Knave</em> rules. If you use another rule system you’ll likely gradually expand on your enums as you figure out what you’ll need).</p>
<p>Here the <codeclass="docutils literal notranslate"><spanclass="pre">Ability</span></code> class holds basic properties of a character sheet.</p>
<p>The <codeclass="docutils literal notranslate"><spanclass="pre">ABILITY_REVERSE_MAP</span></code> is a convenient map to go the other way - if you in some command were to enter the string ‘cha’, we could use this mapping to directly convert your input to the correct <codeclass="docutils literal notranslate"><spanclass="pre">Ability</span></code>:</p>
<p>This is for general functions we may need from all over. In this case we only picture one utility, a function that produces a pretty display of any object we pass to it.</p>
<p>This is an example of the string we want to see:</p>
<spanclass="s2">Slots: |w</span><spanclass="si">{size}</span><spanclass="s2">|n, Used from: |w</span><spanclass="si">{use_slot_name}</span><spanclass="s2">|n</span>
<spanclass="s2">Attacks using |w</span><spanclass="si">{attack_type_name}</span><spanclass="s2">|n against |w</span><spanclass="si">{defense_type_name}</span><spanclass="s2">|n</span>
<p>Here we set up the string template with place holders for where every piece of info should go. Study this string so you understand what it does. The <codeclass="docutils literal notranslate"><spanclass="pre">|c</span></code>, <codeclass="docutils literal notranslate"><spanclass="pre">|y</span></code>, <codeclass="docutils literal notranslate"><spanclass="pre">|w</span></code> and <codeclass="docutils literal notranslate"><spanclass="pre">|n</span></code> markers are <aclass="reference internal"href="../../../Concepts/Colors.html"><spanclass="doc std std-doc">Evennia color markup</span></a> for making the text cyan, yellow, white and neutral-color respectively.</p>
<p>We can guess some things, such that <codeclass="docutils literal notranslate"><spanclass="pre">obj.key</span></code> is the name of the object, and that <codeclass="docutils literal notranslate"><spanclass="pre">obj.db.desc</span></code> will hold its description (this is how it is in default Evennia).</p>
<p>But so far we have not established how to get any of the other properties like <codeclass="docutils literal notranslate"><spanclass="pre">size</span></code> or <codeclass="docutils literal notranslate"><spanclass="pre">attack_type</span></code>. So we just set them to dummy values. We’ll need to get back to this when we have more code in place!</p>
</section>
<sectionid="testing">
<h2><spanclass="section-number">1.4. </span>Testing<aclass="headerlink"href="#testing"title="Permalink to this headline">¶</a></h2>
<blockquote>
<div><p>create a new module <codeclass="docutils literal notranslate"><spanclass="pre">mygame/evadventure/tests/test_utils.py</span></code></p>
</div></blockquote>
<p>How do you know if you made a typo in the code above? You could <em>manually</em> test it by reloading your Evennia server and do the following from in-game:</p>
<divclass="highlight-none notranslate"><divclass="highlight"><pre><span></span>py from evadventure.utils import get_obj_stats;print(get_obj_stats(self))
</pre></div>
</div>
<p>You should get back a nice string about yourself! If that works, great! But you’ll need to remember doing that test when you change this code later.</p>
is an example of the testing module. To dive deeper into unit testing in Evennia, see the <aclass="reference internal"href="../../../Coding/Unit-Testing.html"><spanclass="doc std std-doc">Unit testing</span></a> documentation.</p>
</aside>
<p>A <em>unit test</em> allows you to set up automated testing of code. Once you’ve written your test you can run it over and over and make sure later changes to your code didn’t break things.</p>
<p>In this particular case, we <em>expect</em> to later have to update the test when <codeclass="docutils literal notranslate"><spanclass="pre">get_obj_stats</span></code> becomes more complete and returns more reasonable data.</p>
<p>Evennia comes with extensive functionality to help you test your code. Here’s a module for
<spanclass="n">attributes</span><spanclass="o">=</span><spanclass="p">((</span><spanclass="s2">"desc"</span><spanclass="p">,</span><spanclass="s2">"A test object"</span><spanclass="p">),)</span>
<spanclass="p">)</span>
<spanclass="c1"># run it through the function </span>
<p>What happens here is that we create a new test-class <codeclass="docutils literal notranslate"><spanclass="pre">TestUtils</span></code> that inherits from <codeclass="docutils literal notranslate"><spanclass="pre">EvenniaTest</span></code>. This inheritance is what makes this a testing class.</p>
<divclass="admonition important">
<pclass="admonition-title">Important</p>
<p>It’s useful for any game dev to know how to effectively test their code. So we’ll try to include a <em>Testing</em> section at the end of each of the implementation lessons to follow. Writing tests for your code is optional but highly recommended. It can feel a little cumbersome or time-consuming at first … but you’ll thank yourself later.</p>
</div>
<p>We can have any number of methods on this class. To have a method recognized as one containing code to test, its name <em>must</em> start with <codeclass="docutils literal notranslate"><spanclass="pre">test_</span></code>. We have one - <codeclass="docutils literal notranslate"><spanclass="pre">test_get_obj_stats</span></code>.</p>
<p>In this method we create a dummy <codeclass="docutils literal notranslate"><spanclass="pre">obj</span></code> and gives it a <codeclass="docutils literal notranslate"><spanclass="pre">key</span></code> “testobj”. Note how we add the <codeclass="docutils literal notranslate"><spanclass="pre">desc</span></code><aclass="reference internal"href="../../../Components/Attributes.html"><spanclass="doc std std-doc">Attribute</span></a> directly in the <codeclass="docutils literal notranslate"><spanclass="pre">create_object</span></code> call by specifying the attribute as a tuple <codeclass="docutils literal notranslate"><spanclass="pre">(name,</span><spanclass="pre">value)</span></code>!</p>
<p>We then get the result of passing this dummy-object through <codeclass="docutils literal notranslate"><spanclass="pre">get_obj_stats</span></code> we imported earlier.</p>
<p>The <codeclass="docutils literal notranslate"><spanclass="pre">assertEqual</span></code> method is available on all testing classes and checks that the <codeclass="docutils literal notranslate"><spanclass="pre">result</span></code> is equal to the string we specify. If they are the same, the test <em>passes</em>, otherwise it <em>fails</em> and we need to investigate what went wrong.</p>
<sectionid="running-your-test">
<h3><spanclass="section-number">1.4.1. </span>Running your test<aclass="headerlink"href="#running-your-test"title="Permalink to this headline">¶</a></h3>
<p>To run your test you need to stand inside your <codeclass="docutils literal notranslate"><spanclass="pre">mygame</span></code> folder and execute the following command:</p>
<divclass="highlight-none notranslate"><divclass="highlight"><pre><span></span>evennia test --settings settings.py evadventure.tests
</pre></div>
</div>
<p>This will run all your <codeclass="docutils literal notranslate"><spanclass="pre">evadventure</span></code> tests (if you had more of them). To only run your utility tests you could do</p>
<divclass="highlight-none notranslate"><divclass="highlight"><pre><span></span>evennia test --settings settings.py evadventure.tests.test_utils
</pre></div>
</div>
<p>If all goes well, you should get an <codeclass="docutils literal notranslate"><spanclass="pre">OK</span></code> back. Otherwise you need to check the failure, maybe your return string doesn’t quite match what you expected.</p>
<blockquote>
<div><p>Hint: The example unit test code above contains a deliberate error in capitalization. See if you can interpret the error and fix it!</p>
</div></blockquote>
</section>
</section>
<sectionid="summary">
<h2><spanclass="section-number">1.5. </span>Summary<aclass="headerlink"href="#summary"title="Permalink to this headline">¶</a></h2>
<p>It’s very important to understand how you import code between modules in Python, so if this is still confusing to you, it’s worth to read up on this more.</p>
<p>That said, many newcomers are confused with how to begin, so by creating the folder structure, some small modules and even making your first unit test, you are off to a great start!</p>
<pclass="last">You are reading an old version of the Evennia documentation. <ahref="https://www.evennia.com/docs/latest/index.html">The latest version is here</a></p>.