mirror of
https://github.com/evennia/evennia.git
synced 2026-03-17 13:26:30 +01:00
455 lines
No EOL
39 KiB
HTML
455 lines
No EOL
39 KiB
HTML
<!DOCTYPE html>
|
||
|
||
<html lang="en" data-content_root="../../../">
|
||
<head>
|
||
<meta charset="utf-8" />
|
||
<meta name="viewport" content="width=device-width, initial-scale=1.0" /><meta name="viewport" content="width=device-width, initial-scale=1" />
|
||
|
||
<title>1. Code Structure and Utilities — Evennia latest documentation</title>
|
||
<link rel="stylesheet" type="text/css" href="../../../_static/pygments.css?v=d75fae25" />
|
||
<link rel="stylesheet" type="text/css" href="../../../_static/nature.css?v=279e0f84" />
|
||
<link rel="stylesheet" type="text/css" href="../../../_static/custom.css?v=e4a91a55" />
|
||
<script src="../../../_static/documentation_options.js?v=c6e86fd7"></script>
|
||
<script src="../../../_static/doctools.js?v=9bcbadda"></script>
|
||
<script src="../../../_static/sphinx_highlight.js?v=dc90522c"></script>
|
||
<link rel="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="2. Rules and dice rolling" href="Beginner-Tutorial-Rules.html" />
|
||
<link rel="prev" title="Part 3: How We Get There (Example Game)" href="Beginner-Tutorial-Part3-Overview.html" />
|
||
</head><body>
|
||
<div class="related" role="navigation" aria-label="Related">
|
||
<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-Rules.html" title="2. Rules and dice rolling"
|
||
accesskey="N">next</a> |</li>
|
||
<li class="right" >
|
||
<a href="Beginner-Tutorial-Part3-Overview.html" title="Part 3: How We Get There (Example Game)"
|
||
accesskey="P">previous</a> |</li>
|
||
<li class="nav-item nav-item-0"><a href="../../../index.html">Evennia</a> »</li>
|
||
<li class="nav-item nav-item-1"><a href="../../Howtos-Overview.html" >Tutorials and How-To’s</a> »</li>
|
||
<li class="nav-item nav-item-2"><a href="../Beginner-Tutorial-Overview.html" >Beginner Tutorial</a> »</li>
|
||
<li class="nav-item nav-item-3"><a href="Beginner-Tutorial-Part3-Overview.html" accesskey="U">Part 3: How We Get There (Example Game)</a> »</li>
|
||
<li class="nav-item nav-item-this"><a href=""><span class="section-number">1. </span>Code Structure and Utilities</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="code-structure-and-utilities">
|
||
<h1><span class="section-number">1. </span>Code Structure and Utilities<a class="headerlink" href="#code-structure-and-utilities" title="Link to this heading">¶</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>
|
||
<section id="folder-structure">
|
||
<h2><span class="section-number">1.1. </span>Folder Structure<a class="headerlink" href="#folder-structure" title="Link to this heading">¶</a></h2>
|
||
<aside class="sidebar">
|
||
<p class="sidebar-title">This layout is for the tutorial!</p>
|
||
<p>We make the <code class="docutils literal notranslate"><span class="pre">evadventure</span></code> folder stand-alone for the sake of the tutorial only. Leaving the code isolated in this way makes it clear what we have changed — and for you to grab what you want later. It also makes it easier to refer to the matching code in <code class="docutils literal notranslate"><span class="pre">evennia/contrib/tutorials/evadventure</span></code>.</p>
|
||
<p>For your own game, you are instead encouraged to modify your game dir in-place (i.e, directly add to <code class="docutils literal notranslate"><span class="pre">commands/commands.py</span></code> and modify the <code class="docutils literal notranslate"><span class="pre">typeclasses/</span></code> modules directly). Except for the <code class="docutils literal notranslate"><span class="pre">server/</span></code> folder, you are, in fact, free to structure your game dir code pretty much as you like.</p>
|
||
</aside>
|
||
<p>Create a new folder named <code class="docutils literal notranslate"><span class="pre">evadventure</span></code> under your <code class="docutils literal notranslate"><span class="pre">mygame</span></code> folder. Inside it the new folder, create another folder named <code class="docutils literal notranslate"><span class="pre">tests/</span></code>. Make sure to put empty <code class="docutils literal notranslate"><span class="pre">__init__.py</span></code> files in both new folders. Doing so turns both new folders into packages from which Python understands to import automatically.</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">mygame</span><span class="o">/</span>
|
||
<span class="n">commands</span><span class="o">/</span>
|
||
<span class="n">evadventure</span><span class="o">/</span> <span class="o"><---</span>
|
||
<span class="fm">__init__</span><span class="o">.</span><span class="n">py</span> <span class="o"><---</span>
|
||
<span class="n">tests</span><span class="o">/</span> <span class="o"><---</span>
|
||
<span class="fm">__init__</span><span class="o">.</span><span class="n">py</span> <span class="o"><---</span>
|
||
<span class="fm">__init__</span><span class="o">.</span><span class="n">py</span>
|
||
<span class="n">README</span><span class="o">.</span><span class="n">md</span>
|
||
<span class="n">server</span><span class="o">/</span>
|
||
<span class="n">typeclasses</span><span class="o">/</span>
|
||
<span class="n">web</span><span class="o">/</span>
|
||
<span class="n">world</span><span class="o">/</span>
|
||
|
||
</pre></div>
|
||
</div>
|
||
<p>Importing anything from inside this folder from anywhere else under <code class="docutils literal notranslate"><span class="pre">mygame</span></code> will be done by</p>
|
||
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="c1"># from anywhere in mygame/</span>
|
||
<span class="kn">from</span><span class="w"> </span><span class="nn">evadventure.yourmodulename</span><span class="w"> </span><span class="kn">import</span> <span class="n">whatever</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>This is the ‘absolute path` type of import.</p>
|
||
<p>Between two modules both in <code class="docutils literal notranslate"><span class="pre">evadventure/</span></code>, you can use a ‘relative’ import with <code class="docutils literal notranslate"><span class="pre">.</span></code>:</p>
|
||
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="c1"># from a module inside mygame/evadventure</span>
|
||
<span class="kn">from</span><span class="w"> </span><span class="nn">.yourmodulename</span><span class="w"> </span><span class="kn">import</span> <span class="n">whatever</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>From e.g. inside <code class="docutils literal notranslate"><span class="pre">mygame/evadventure/tests/</span></code> you can import from one level above using <code class="docutils literal notranslate"><span class="pre">..</span></code>:</p>
|
||
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="c1"># from mygame/evadventure/tests/</span>
|
||
<span class="kn">from</span><span class="w"> </span><span class="nn">..yourmodulename</span><span class="w"> </span><span class="kn">import</span> <span class="n">whatever</span>
|
||
</pre></div>
|
||
</div>
|
||
</section>
|
||
<section id="enums">
|
||
<h2><span class="section-number">1.2. </span>Enums<a class="headerlink" href="#enums" title="Link to this heading">¶</a></h2>
|
||
<aside class="sidebar">
|
||
<p>A full example of the enum module is found in
|
||
<a class="reference internal" href="../../../api/evennia.contrib.tutorials.evadventure.enums.html"><span class="std std-doc">evennia/contrib/tutorials/evadventure/enums.py</span></a>.</p>
|
||
</aside>
|
||
<p>Create a new file <code class="docutils literal notranslate"><span class="pre">mygame/evadventure/enums.py</span></code>.</p>
|
||
<p>An <a class="reference external" href="https://docs.python.org/3/library/enum.html">enum</a> (enumeration) is a way to establish constants in Python. For example:</p>
|
||
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="c1"># in a file mygame/evadventure/enums.py</span>
|
||
|
||
<span class="kn">from</span><span class="w"> </span><span class="nn">enum</span><span class="w"> </span><span class="kn">import</span> <span class="n">Enum</span>
|
||
|
||
<span class="k">class</span><span class="w"> </span><span class="nc">Ability</span><span class="p">(</span><span class="n">Enum</span><span class="p">):</span>
|
||
|
||
<span class="n">STR</span> <span class="o">=</span> <span class="s2">"strength"</span>
|
||
|
||
</pre></div>
|
||
</div>
|
||
<p>You can then access an enum like this:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="c1"># from another module in mygame/evadventure</span>
|
||
|
||
<span class="kn">from</span><span class="w"> </span><span class="nn">.enums</span><span class="w"> </span><span class="kn">import</span> <span class="n">Ability</span>
|
||
|
||
<span class="n">Ability</span><span class="o">.</span><span class="n">STR</span> <span class="c1"># the enum itself</span>
|
||
<span class="n">Ability</span><span class="o">.</span><span class="n">STR</span><span class="o">.</span><span class="n">value</span> <span class="c1"># this is the string "strength"</span>
|
||
|
||
</pre></div>
|
||
</div>
|
||
<p>Using enums is a recommended practice. With enums set up, we can make sure to refer to the same constant or variable every time. Keeping all enums in one place also means we have a good overview of the constants with which we are dealing.</p>
|
||
<p>The alternative to enums would be, for example, to pass around a string named <code class="docutils literal notranslate"><span class="pre">"constitution"</span></code>. If you mis-spelled this as, say, <code class="docutils literal notranslate"><span class="pre">"consitution"</span></code>, you would not necessarily know it right away because the error would happen later when the string is not recognized. By using the enum practice,should you make a typo getting <code class="docutils literal notranslate"><span class="pre">Ability.COM</span></code> instead of <code class="docutils literal notranslate"><span class="pre">Ability.CON</span></code>, Python will immediately raise an error becase this enum with the typo will not be recognized.</p>
|
||
<p>With enums, you can also do nice direct comparisons like <code class="docutils literal notranslate"><span class="pre">if</span> <span class="pre">ability</span> <span class="pre">is</span> <span class="pre">Ability.WIS:</span> <span class="pre"><do</span> <span class="pre">stuff></span></code>.</p>
|
||
<p>Note that the <code class="docutils literal notranslate"><span class="pre">Ability.STR</span></code> enum does not have the actual <em>value</em> of, for instance, your Strength. <code class="docutils literal notranslate"><span class="pre">Ability.STR</span></code> is just a fixed label for the Strength ability.</p>
|
||
<p>Below is the <code class="docutils literal notranslate"><span class="pre">enum.py</span></code> module needed for <em>Knave</em>. It covers the basic aspects of the rule system we need to track. (Check out the <em>Knave</em> rules.) Should you later use another rule system, you’ll likely expand on your enums gradually as you figure out what you’ll need.</p>
|
||
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="c1"># mygame/evadventure/enums.py</span>
|
||
|
||
<span class="k">class</span><span class="w"> </span><span class="nc">Ability</span><span class="p">(</span><span class="n">Enum</span><span class="p">):</span>
|
||
<span class="w"> </span><span class="sd">"""</span>
|
||
<span class="sd"> The six base ability-bonuses and other</span>
|
||
<span class="sd"> abilities</span>
|
||
|
||
<span class="sd"> """</span>
|
||
|
||
<span class="n">STR</span> <span class="o">=</span> <span class="s2">"strength"</span>
|
||
<span class="n">DEX</span> <span class="o">=</span> <span class="s2">"dexterity"</span>
|
||
<span class="n">CON</span> <span class="o">=</span> <span class="s2">"constitution"</span>
|
||
<span class="n">INT</span> <span class="o">=</span> <span class="s2">"intelligence"</span>
|
||
<span class="n">WIS</span> <span class="o">=</span> <span class="s2">"wisdom"</span>
|
||
<span class="n">CHA</span> <span class="o">=</span> <span class="s2">"charisma"</span>
|
||
|
||
<span class="n">ARMOR</span> <span class="o">=</span> <span class="s2">"armor"</span>
|
||
|
||
<span class="n">CRITICAL_FAILURE</span> <span class="o">=</span> <span class="s2">"critical_failure"</span>
|
||
<span class="n">CRITICAL_SUCCESS</span> <span class="o">=</span> <span class="s2">"critical_success"</span>
|
||
|
||
<span class="n">ALLEGIANCE_HOSTILE</span> <span class="o">=</span> <span class="s2">"hostile"</span>
|
||
<span class="n">ALLEGIANCE_NEUTRAL</span> <span class="o">=</span> <span class="s2">"neutral"</span>
|
||
<span class="n">ALLEGIANCE_FRIENDLY</span> <span class="o">=</span> <span class="s2">"friendly"</span>
|
||
|
||
|
||
<span class="n">ABILITY_REVERSE_MAP</span> <span class="o">=</span> <span class="p">{</span>
|
||
<span class="s2">"str"</span><span class="p">:</span> <span class="n">Ability</span><span class="o">.</span><span class="n">STR</span><span class="p">,</span>
|
||
<span class="s2">"dex"</span><span class="p">:</span> <span class="n">Ability</span><span class="o">.</span><span class="n">DEX</span><span class="p">,</span>
|
||
<span class="s2">"con"</span><span class="p">:</span> <span class="n">Ability</span><span class="o">.</span><span class="n">CON</span><span class="p">,</span>
|
||
<span class="s2">"int"</span><span class="p">:</span> <span class="n">Ability</span><span class="o">.</span><span class="n">INT</span><span class="p">,</span>
|
||
<span class="s2">"wis"</span><span class="p">:</span> <span class="n">Ability</span><span class="o">.</span><span class="n">WIS</span><span class="p">,</span>
|
||
<span class="s2">"cha"</span><span class="p">:</span> <span class="n">Ability</span><span class="o">.</span><span class="n">CHA</span>
|
||
<span class="p">}</span>
|
||
|
||
</pre></div>
|
||
</div>
|
||
<p>Above, the <code class="docutils literal notranslate"><span class="pre">Ability</span></code> class holds some basic properties of a character sheet.</p>
|
||
<p>The <code class="docutils literal notranslate"><span class="pre">ABILITY_REVERSE_MAP</span></code> is a convenient map to convert a string to an Enum. The most common use of this would be in a Command; the Player don’t know anything about Enums, they can only send strings. So we’d only get the string “cha”. Using this <code class="docutils literal notranslate"><span class="pre">ABILITY_REVERSE_MAP</span></code> we can conveniently convert this input to an <code class="docutils literal notranslate"><span class="pre">Ability.CHA</span></code> Enum you can then pass around in code</p>
|
||
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>ability = ABILITY_REVERSE_MAP.get(user_input)
|
||
</pre></div>
|
||
</div>
|
||
</section>
|
||
<section id="utility-module">
|
||
<h2><span class="section-number">1.3. </span>Utility Module<a class="headerlink" href="#utility-module" title="Link to this heading">¶</a></h2>
|
||
<blockquote>
|
||
<div><p>Create a new module <code class="docutils literal notranslate"><span class="pre">mygame/evadventure/utils.py</span></code></p>
|
||
</div></blockquote>
|
||
<aside class="sidebar">
|
||
<p>An example of the utility module is found in
|
||
<a class="reference internal" href="../../../api/evennia.contrib.tutorials.evadventure.utils.html"><span class="std std-doc">evennia/contrib/tutorials/evadventure/utils.py</span></a></p>
|
||
</aside>
|
||
<p>The utility module is used to contain general functions we may need to call repeatedly from various other modules. In this tutorial example, we only crate one utility: a function that produces a pretty display of any object we pass to it.</p>
|
||
<p>Here’s how it could look:</p>
|
||
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="c1"># in mygame/evadventure/utils.py</span>
|
||
|
||
<span class="n">_OBJ_STATS</span> <span class="o">=</span> <span class="s2">"""</span>
|
||
<span class="s2">|c</span><span class="si">{key}</span><span class="s2">|n</span>
|
||
<span class="s2">Value: ~|y</span><span class="si">{value}</span><span class="s2">|n coins</span><span class="si">{carried}</span>
|
||
|
||
<span class="si">{desc}</span>
|
||
|
||
<span class="s2">Slots: |w</span><span class="si">{size}</span><span class="s2">|n, Used from: |w</span><span class="si">{use_slot_name}</span><span class="s2">|n</span>
|
||
<span class="s2">Quality: |w</span><span class="si">{quality}</span><span class="s2">|n, Uses: |w</span><span class="si">{uses}</span><span class="s2">|n</span>
|
||
<span class="s2">Attacks using |w</span><span class="si">{attack_type_name}</span><span class="s2">|n against |w</span><span class="si">{defense_type_name}</span><span class="s2">|n</span>
|
||
<span class="s2">Damage roll: |w</span><span class="si">{damage_roll}</span><span class="s2">|n</span>
|
||
<span class="s2">"""</span><span class="o">.</span><span class="n">strip</span><span class="p">()</span>
|
||
|
||
|
||
<span class="k">def</span><span class="w"> </span><span class="nf">get_obj_stats</span><span class="p">(</span><span class="n">obj</span><span class="p">,</span> <span class="n">owner</span><span class="o">=</span><span class="kc">None</span><span class="p">):</span>
|
||
<span class="w"> </span><span class="sd">"""</span>
|
||
<span class="sd"> Get a string of stats about the object.</span>
|
||
|
||
<span class="sd"> Args:</span>
|
||
<span class="sd"> obj (Object): The object to get stats for.</span>
|
||
<span class="sd"> owner (Object): The one currently owning/carrying `obj`, if any. Can be</span>
|
||
<span class="sd"> used to show e.g. where they are wielding it.</span>
|
||
<span class="sd"> Returns:</span>
|
||
<span class="sd"> str: A nice info string to display about the object.</span>
|
||
|
||
<span class="sd"> """</span>
|
||
<span class="k">return</span> <span class="n">_OBJ_STATS</span><span class="o">.</span><span class="n">format</span><span class="p">(</span>
|
||
<span class="n">key</span><span class="o">=</span><span class="n">obj</span><span class="o">.</span><span class="n">key</span><span class="p">,</span>
|
||
<span class="n">value</span><span class="o">=</span><span class="mi">10</span><span class="p">,</span>
|
||
<span class="n">carried</span><span class="o">=</span><span class="s2">"[Not carried]"</span><span class="p">,</span>
|
||
<span class="n">desc</span><span class="o">=</span><span class="n">obj</span><span class="o">.</span><span class="n">db</span><span class="o">.</span><span class="n">desc</span><span class="p">,</span>
|
||
<span class="n">size</span><span class="o">=</span><span class="mi">1</span><span class="p">,</span>
|
||
<span class="n">quality</span><span class="o">=</span><span class="mi">3</span><span class="p">,</span>
|
||
<span class="n">uses</span><span class="o">=</span><span class="s2">"infinite"</span><span class="p">,</span>
|
||
<span class="n">use_slot_name</span><span class="o">=</span><span class="s2">"backpack"</span><span class="p">,</span>
|
||
<span class="n">attack_type_name</span><span class="o">=</span><span class="s2">"strength"</span><span class="p">,</span>
|
||
<span class="n">defense_type_name</span><span class="o">=</span><span class="s2">"armor"</span><span class="p">,</span>
|
||
<span class="n">damage_roll</span><span class="o">=</span><span class="s2">"1d6"</span>
|
||
<span class="p">)</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>Previously throughout these tutorial lessons, we have seen the <code class="docutils literal notranslate"><span class="pre">"""</span> <span class="pre">...</span> <span class="pre">"""</span></code> multi-line string used mainly for function help strings, but a triple-quoted string in Python is used for any multi-line string.</p>
|
||
<p>Above, we set up a string template (<code class="docutils literal notranslate"><span class="pre">_OBJ_STATS</span></code>) with place holders (<code class="docutils literal notranslate"><span class="pre">{...}</span></code>) for where every element of stats information should go. In the <code class="docutils literal notranslate"><span class="pre">_OBJ_STATS.format(...)</span></code> call, we then dynamically fill those place holders with data from the object we pass into <code class="docutils literal notranslate"><span class="pre">get_obj_stats</span></code>.</p>
|
||
<p>Here’s what you’d get back if you were to pass a ‘chipped sword’ to <code class="docutils literal notranslate"><span class="pre">get_obj_stats</span></code> (note that these docs don’t show the text colors):</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">Chipped</span> <span class="n">Sword</span>
|
||
<span class="n">Value</span><span class="p">:</span> <span class="o">~</span><span class="mi">10</span> <span class="n">coins</span> <span class="p">[</span><span class="n">wielded</span> <span class="ow">in</span> <span class="n">Weapon</span> <span class="n">hand</span><span class="p">]</span>
|
||
|
||
<span class="n">A</span> <span class="n">simple</span> <span class="n">sword</span> <span class="n">used</span> <span class="n">by</span> <span class="n">mercenaries</span> <span class="nb">all</span> <span class="n">over</span>
|
||
<span class="n">the</span> <span class="n">world</span><span class="o">.</span>
|
||
|
||
<span class="n">Slots</span><span class="p">:</span> <span class="mi">1</span><span class="p">,</span> <span class="n">Used</span> <span class="n">from</span><span class="p">:</span> <span class="n">weapon</span> <span class="n">hand</span>
|
||
<span class="n">Quality</span><span class="p">:</span> <span class="mi">3</span><span class="p">,</span> <span class="n">Uses</span><span class="p">:</span> <span class="kc">None</span>
|
||
<span class="n">Attacks</span> <span class="n">using</span> <span class="n">strength</span> <span class="n">against</span> <span class="n">armor</span><span class="o">.</span>
|
||
<span class="n">Damage</span> <span class="n">roll</span><span class="p">:</span> <span class="mi">1</span><span class="n">d6</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>We will later use this to let the player inspect any object without us having to make a new utility for every object type.</p>
|
||
<p>Study the <code class="docutils literal notranslate"><span class="pre">_OBJ_STATS</span></code> template string so that you understand what it does. The <code class="docutils literal notranslate"><span class="pre">|c</span></code>, <code class="docutils literal notranslate"><span class="pre">|y</span></code>, <code class="docutils literal notranslate"><span class="pre">|w</span></code> and <code class="docutils literal notranslate"><span class="pre">|n</span></code> markers are <a class="reference internal" href="../../../Concepts/Colors.html"><span class="std std-doc">Evennia color markup</span></a> for making the text cyan, yellow, white and neutral-color, respectively.</p>
|
||
<p>Some stats elements are easy to identify in the above code. For instance, <code class="docutils literal notranslate"><span class="pre">obj.key</span></code> is the name of an object and <code class="docutils literal notranslate"><span class="pre">obj.db.desc</span></code> will hold an object’s description — this is also how default Evennia works.</p>
|
||
<p>So far, here in our tutorial, we have not yet established how to get any of the other properties like <code class="docutils literal notranslate"><span class="pre">size</span></code>, <code class="docutils literal notranslate"><span class="pre">damage_roll</span></code> or <code class="docutils literal notranslate"><span class="pre">attack_type_name</span></code>. For our current purposes, we will just set them to fixed dummy values so they work. We’ll need to revisit them later when we have more code in place!</p>
|
||
</section>
|
||
<section id="testing">
|
||
<h2><span class="section-number">1.4. </span>Testing<a class="headerlink" href="#testing" title="Link to this heading">¶</a></h2>
|
||
<p>Evennia comes with extensive functionality to help you test your code. A <em>unit test</em> allows you to set up automated testing of code. Once you’ve written your test, you can then run it over and over again to ensure later changes to your code didn’t break things by introducing errors.</p>
|
||
<blockquote>
|
||
<div><p>create a new module <code class="docutils literal notranslate"><span class="pre">mygame/evadventure/tests/test_utils.py</span></code></p>
|
||
</div></blockquote>
|
||
<p>How would you know if you made a typo in the code above? You can <em>manually</em> test it by reloading your Evennia server and issuing the following in-game python command:</p>
|
||
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>py from evadventure.utils import get_obj_stats;print(get_obj_stats(self))
|
||
</pre></div>
|
||
</div>
|
||
<p>Doing so should spit back a nice bit of string ouput about yourself! If that works, great! But, you’ll need to remember re-running that test manually when you later change the code.</p>
|
||
<aside class="sidebar">
|
||
<p>In <a class="reference internal" href="../../../api/evennia.contrib.tutorials.evadventure.tests.test_utils.html#evennia-contrib-tutorials-evadventure-tests-test-utils"><span class="std std-ref">evennia/contrib/tutorials/evadventure/tests/test_utils.py</span></a>
|
||
is an example of the testing module. To dive deeper into unit testing in Evennia, see the <a class="reference internal" href="../../../Coding/Unit-Testing.html"><span class="std std-doc">Unit testing</span></a> documentation.</p>
|
||
</aside>
|
||
<p>In our particular case of this tutorial, we should <em>expect</em> to need to later update the test when the <code class="docutils literal notranslate"><span class="pre">get_obj_stats</span></code> code becomes more complete and returns more pertinent data.</p>
|
||
<p>Here’s a module for testing <code class="docutils literal notranslate"><span class="pre">get_obj_stats</span></code>.</p>
|
||
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="c1"># mygame/evadventure/tests/test_utils.py</span>
|
||
|
||
<span class="kn">from</span><span class="w"> </span><span class="nn">evennia.utils</span><span class="w"> </span><span class="kn">import</span> <span class="n">create</span>
|
||
<span class="kn">from</span><span class="w"> </span><span class="nn">evennia.utils.test_resources</span><span class="w"> </span><span class="kn">import</span> <span class="n">EvenniaTest</span>
|
||
|
||
<span class="kn">from</span><span class="w"> </span><span class="nn">..import</span> <span class="n">utils</span>
|
||
|
||
<span class="k">class</span><span class="w"> </span><span class="nc">TestUtils</span><span class="p">(</span><span class="n">EvenniaTest</span><span class="p">):</span>
|
||
<span class="k">def</span><span class="w"> </span><span class="nf">test_get_obj_stats</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
|
||
<span class="c1"># make a simple object to test with</span>
|
||
<span class="n">obj</span> <span class="o">=</span> <span class="n">create</span><span class="o">.</span><span class="n">create_object</span><span class="p">(</span>
|
||
<span class="n">key</span><span class="o">=</span><span class="s2">"testobj"</span><span class="p">,</span>
|
||
<span class="n">attributes</span><span class="o">=</span><span class="p">((</span><span class="s2">"desc"</span><span class="p">,</span> <span class="s2">"A test object"</span><span class="p">),)</span>
|
||
<span class="p">)</span>
|
||
<span class="c1"># run it through the function</span>
|
||
<span class="n">result</span> <span class="o">=</span> <span class="n">utils</span><span class="o">.</span><span class="n">get_obj_stats</span><span class="p">(</span><span class="n">obj</span><span class="p">)</span>
|
||
<span class="c1"># check that the result is what we expected</span>
|
||
<span class="bp">self</span><span class="o">.</span><span class="n">assertEqual</span><span class="p">(</span>
|
||
<span class="n">result</span><span class="p">,</span>
|
||
<span class="w"> </span><span class="sd">"""</span>
|
||
<span class="sd">|ctestobj|n</span>
|
||
<span class="sd">Value: ~|y10|n coins[Not carried]</span>
|
||
|
||
<span class="sd">A test object</span>
|
||
|
||
<span class="sd">Slots: |w1|n, Used from: |wbackpack|n</span>
|
||
<span class="sd">Quality: |w3|n, Uses: |winfinite|n</span>
|
||
<span class="sd">Attacks using |wstrength|n against |warmor|n</span>
|
||
<span class="sd">Damage roll: |w1d6|n</span>
|
||
<span class="sd">"""</span><span class="o">.</span><span class="n">strip</span><span class="p">()</span>
|
||
<span class="p">)</span>
|
||
|
||
</pre></div>
|
||
</div>
|
||
<p>What happens in the above code is that we create a new test-class named <code class="docutils literal notranslate"><span class="pre">TestUtils</span></code> that inherits from <code class="docutils literal notranslate"><span class="pre">EvenniaTest</span></code>. It is this inheritance that makes this a testing class.</p>
|
||
<div class="admonition important">
|
||
<p class="admonition-title">Important</p>
|
||
<p>It’s useful for any game dev to know how to test their code effectively. So, we’ll try to include a <em>Testing</em> section at the end of each implementation lesson that follows in this tutorial.</p>
|
||
<p>Writing tests for your code is optional, but highly recommended. Initially, unit testing may feel a little cumbersome or time-consuming… but you’ll thank yourself later.</p>
|
||
</div>
|
||
<p>We can have any number of methods called on this class. To have Evennia automatically recognize a method as one containing code to test, its name <em>must</em> start with the <code class="docutils literal notranslate"><span class="pre">test_</span></code> prefix. We have one here as <code class="docutils literal notranslate"><span class="pre">test_get_obj_stats</span></code>.</p>
|
||
<p>In our <code class="docutils literal notranslate"><span class="pre">test_get_obj_stats</span></code> method, we create a dummy <code class="docutils literal notranslate"><span class="pre">obj</span></code> and assign it a <code class="docutils literal notranslate"><span class="pre">key</span></code> “testobj”. Note that we add the<code class="docutils literal notranslate"><span class="pre">desc</span></code> <a class="reference internal" href="../../../Components/Attributes.html"><span class="std std-doc">Attribute</span></a> directly in the <code class="docutils literal notranslate"><span class="pre">create_object</span></code> call by specifying the attribute as a tuple <code class="docutils literal notranslate"><span class="pre">(name,</span> <span class="pre">value)</span></code>!</p>
|
||
<p>Then, we can get the result of passing this dummy-object through the <code class="docutils literal notranslate"><span class="pre">get_obj_stats</span></code> function that we imported earlier.</p>
|
||
<p>The <code class="docutils literal notranslate"><span class="pre">assertEqual</span></code> method is available on all testing classes and checks that the <code class="docutils literal notranslate"><span class="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>
|
||
<section id="running-your-test">
|
||
<h3><span class="section-number">1.4.1. </span>Running your Test<a class="headerlink" href="#running-your-test" title="Link to this heading">¶</a></h3>
|
||
<p>To run our utility module test, we need to issue the following command directly from the <code class="docutils literal notranslate"><span class="pre">mygame</span></code> folder:</p>
|
||
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>evennia test --settings settings.py evadventure.tests
|
||
</pre></div>
|
||
</div>
|
||
<p>The above command will run all <code class="docutils literal notranslate"><span class="pre">evadventure</span></code> tests found in the <code class="docutils literal notranslate"><span class="pre">mygame/evadventure/tests</span></code> folder. To run only our utility tests we might instead specify the test individually:</p>
|
||
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>evennia test --settings settings.py evadventure.tests.test_utils
|
||
</pre></div>
|
||
</div>
|
||
<p>If all goes well, the above utility test should produce output ending with <code class="docutils literal notranslate"><span class="pre">OK</span></code> to indicate our code has passed the test.</p>
|
||
<p>However, if our return string doesn’t quite match what we expected, the test will fail. We will then need to begin examining and troubleshooting our failing code.</p>
|
||
<blockquote>
|
||
<div><p>Hint: The above example unit test code contains a deliberate error in capitalization. See if you can examine the output to interpret the deliberate error, and then fix it!</p>
|
||
</div></blockquote>
|
||
</section>
|
||
</section>
|
||
<section id="summary">
|
||
<h2><span class="section-number">1.5. </span>Summary<a class="headerlink" href="#summary" title="Link to this heading">¶</a></h2>
|
||
<p>It’s very important to understand how you import code among modules in Python. If importing from Python modules is still confusing to you, it’s worth it to read more on the topic.</p>
|
||
<p>That said, many newcomers are confused with how to tackle these concepts. In this lesson, by creating the folder structure, two small modules and even making our first unit test, you are off to a great start!</p>
|
||
</section>
|
||
</section>
|
||
|
||
|
||
<div class="clearer"></div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="sphinxsidebar" role="navigation" aria-label="Main">
|
||
<div class="sphinxsidebarwrapper">
|
||
<p class="logo"><a href="../../../index.html">
|
||
<img class="logo" src="../../../_static/evennia_logo.png" alt="Logo of Evennia"/>
|
||
</a></p>
|
||
<search 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" autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false"/>
|
||
<input type="submit" value="Go" />
|
||
</form>
|
||
</div>
|
||
</search>
|
||
<script>document.getElementById('searchbox').style.display = "block"</script>
|
||
<h3><a href="../../../index.html">Table of Contents</a></h3>
|
||
<ul>
|
||
<li><a class="reference internal" href="#">1. Code Structure and Utilities</a><ul>
|
||
<li><a class="reference internal" href="#folder-structure">1.1. Folder Structure</a></li>
|
||
<li><a class="reference internal" href="#enums">1.2. Enums</a></li>
|
||
<li><a class="reference internal" href="#utility-module">1.3. Utility Module</a></li>
|
||
<li><a class="reference internal" href="#testing">1.4. Testing</a><ul>
|
||
<li><a class="reference internal" href="#running-your-test">1.4.1. Running your Test</a></li>
|
||
</ul>
|
||
</li>
|
||
<li><a class="reference internal" href="#summary">1.5. Summary</a></li>
|
||
</ul>
|
||
</li>
|
||
</ul>
|
||
|
||
<div>
|
||
<h4>Previous topic</h4>
|
||
<p class="topless"><a href="Beginner-Tutorial-Part3-Overview.html"
|
||
title="previous chapter">Part 3: How We Get There (Example Game)</a></p>
|
||
</div>
|
||
<div>
|
||
<h4>Next topic</h4>
|
||
<p class="topless"><a href="Beginner-Tutorial-Rules.html"
|
||
title="next chapter"><span class="section-number">2. </span>Rules and dice rolling</a></p>
|
||
</div>
|
||
<div role="note" aria-label="source link">
|
||
<!--h3>This Page</h3-->
|
||
<ul class="this-page-menu">
|
||
<li><a href="../../../_sources/Howtos/Beginner-Tutorial/Part3/Beginner-Tutorial-Utilities.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="https://www.evennia.com/docs/latest/index.html">latest (main branch)</a>
|
||
</li>
|
||
|
||
|
||
<li>
|
||
<a href="https://www.evennia.com/docs/5.x/index.html">v5.0.0 branch (outdated)</a>
|
||
</li>
|
||
|
||
<li>
|
||
<a href="https://www.evennia.com/docs/4.x/index.html">v4.0.0 branch (outdated)</a>
|
||
</li>
|
||
|
||
<li>
|
||
<a href="https://www.evennia.com/docs/3.x/index.html">v3.0.0 branch (outdated)</a>
|
||
</li>
|
||
|
||
<li>
|
||
<a href="https://www.evennia.com/docs/2.x/index.html">v2.0.0 branch (outdated)</a>
|
||
</li>
|
||
|
||
<li>
|
||
<a href="https://www.evennia.com/docs/1.x/index.html">v1.0.0 branch (outdated)</a>
|
||
</li>
|
||
|
||
<li>
|
||
<a href="https://www.evennia.com/docs/0.x/index.html">v0.9.5 branch (outdated)</a>
|
||
</li>
|
||
|
||
</ul>
|
||
|
||
</div>
|
||
</div>
|
||
<div class="clearer"></div>
|
||
</div>
|
||
<div class="related" role="navigation" aria-label="Related">
|
||
<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-Rules.html" title="2. Rules and dice rolling"
|
||
>next</a> |</li>
|
||
<li class="right" >
|
||
<a href="Beginner-Tutorial-Part3-Overview.html" title="Part 3: How We Get There (Example Game)"
|
||
>previous</a> |</li>
|
||
<li class="nav-item nav-item-0"><a href="../../../index.html">Evennia</a> »</li>
|
||
<li class="nav-item nav-item-1"><a href="../../Howtos-Overview.html" >Tutorials and How-To’s</a> »</li>
|
||
<li class="nav-item nav-item-2"><a href="../Beginner-Tutorial-Overview.html" >Beginner Tutorial</a> »</li>
|
||
<li class="nav-item nav-item-3"><a href="Beginner-Tutorial-Part3-Overview.html" >Part 3: How We Get There (Example Game)</a> »</li>
|
||
<li class="nav-item nav-item-this"><a href=""><span class="section-number">1. </span>Code Structure and Utilities</a></li>
|
||
</ul>
|
||
</div>
|
||
<div class="footer" role="contentinfo">
|
||
© Copyright 2024, The Evennia developer community.
|
||
Created using <a href="https://www.sphinx-doc.org/">Sphinx</a> 8.2.3.
|
||
</div>
|
||
</body>
|
||
</html> |