evennia/docs/latest/Howtos/Howto-Command-Duration.html
Evennia docbuilder action d17f22fc2c Updated HTML docs.
2024-03-17 13:48:03 +00:00

571 lines
No EOL
45 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters

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

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" /><meta name="generator" content="Docutils 0.17.1: http://docutils.sourceforge.net/" />
<title>Commands that take time to finish &#8212; Evennia latest documentation</title>
<link rel="stylesheet" href="../_static/nature.css" type="text/css" />
<link rel="stylesheet" href="../_static/pygments.css" type="text/css" />
<script id="documentation_options" data-url_root="../" src="../_static/documentation_options.js"></script>
<script src="../_static/jquery.js"></script>
<script src="../_static/underscore.js"></script>
<script src="../_static/doctools.js"></script>
<script src="../_static/language_data.js"></script>
<link rel="shortcut icon" href="../_static/favicon.ico"/>
<link rel="index" title="Index" href="../genindex.html" />
<link rel="search" title="Search" href="../search.html" />
<link rel="next" title="Return custom errors on missing Exits" href="Howto-Default-Exit-Errors.html" />
<link rel="prev" title="Adding Command Cooldowns" href="Howto-Command-Cooldown.html" />
</head><body>
<div class="related" role="navigation" aria-label="related navigation">
<h3>Navigation</h3>
<ul>
<li class="right" style="margin-right: 10px">
<a href="../genindex.html" title="General Index"
accesskey="I">index</a></li>
<li class="right" >
<a href="../py-modindex.html" title="Python Module Index"
>modules</a> |</li>
<li class="right" >
<a href="Howto-Default-Exit-Errors.html" title="Return custom errors on missing Exits"
accesskey="N">next</a> |</li>
<li class="right" >
<a href="Howto-Command-Cooldown.html" title="Adding Command Cooldowns"
accesskey="P">previous</a> |</li>
<li class="nav-item nav-item-0"><a href="../index.html">Evennia latest</a> &#187;</li>
<li class="nav-item nav-item-1"><a href="Howtos-Overview.html" accesskey="U">Tutorials and How-Tos</a> &#187;</li>
<li class="nav-item nav-item-this"><a href="">Commands that take time to finish</a></li>
</ul>
</div>
<div class="document">
<div class="documentwrapper">
<div class="sphinxsidebar" role="navigation" aria-label="main navigation">
<div class="sphinxsidebarwrapper">
<p class="logo"><a href="../index.html">
<img class="logo" src="../_static/evennia_logo.png" alt="Logo"/>
</a></p>
<div id="searchbox" style="display: none" role="search">
<h3 id="searchlabel">Quick search</h3>
<div class="searchformwrapper">
<form class="search" action="../search.html" method="get">
<input type="text" name="q" aria-labelledby="searchlabel" />
<input type="submit" value="Go" />
</form>
</div>
</div>
<script>$('#searchbox').show(0);</script>
<h3><a href="../index.html">Table of Contents</a></h3>
<ul>
<li><a class="reference internal" href="#">Commands that take time to finish</a><ul>
<li><a class="reference internal" href="#pause-commands-with-yield">Pause commands with <code class="docutils literal notranslate"><span class="pre">yield</span></code></a></li>
<li><a class="reference internal" href="#pause-commands-with-utils-delay">Pause commands with <code class="docutils literal notranslate"><span class="pre">utils.delay</span></code></a></li>
<li><a class="reference internal" href="#making-a-blocking-command">Making a blocking command</a></li>
<li><a class="reference internal" href="#make-a-command-possible-to-abort">Make a Command possible to Abort</a></li>
</ul>
</li>
</ul>
<h4>Previous topic</h4>
<p class="topless"><a href="Howto-Command-Cooldown.html"
title="previous chapter">Adding Command Cooldowns</a></p>
<h4>Next topic</h4>
<p class="topless"><a href="Howto-Default-Exit-Errors.html"
title="next chapter">Return custom errors on missing Exits</a></p>
<div role="note" aria-label="source link">
<!--h3>This Page</h3-->
<ul class="this-page-menu">
<li><a href="../_sources/Howtos/Howto-Command-Duration.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="Howto-Command-Duration.html">latest (main branch)</a></li>
<li><a href="../4.x/index.html">v4.0.0 branch (outdated)</a></li>
<li><a href="../3.x/index.html">v3.0.0 branch (outdated)</a></li>
<li><a href="../2.x/index.html">v2.0.0 branch (outdated)</a></li>
<li><a href="../1.x/index.html">v1.0.0 branch (outdated)</a></li>
<li><a href="../0.x/index.html">v0.9.5 branch (outdated)</a></li>
</ul>
</div>
</div>
<div class="bodywrapper">
<div class="body" role="main">
<section class="tex2jax_ignore mathjax_ignore" id="commands-that-take-time-to-finish">
<h1>Commands that take time to finish<a class="headerlink" href="#commands-that-take-time-to-finish" title="Permalink to this headline"></a></h1>
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>&gt; craft fine sword
You start crafting a fine sword.
&gt; north
You are too focused on your crafting, and can&#39;t move!
You create the blade of the sword.
You create the pommel of the sword.
You finish crafting a Fine Sword.
</pre></div>
</div>
<p>In some types of games a command should not start and finish immediately.</p>
<p>Loading a crossbow might take a bit of time to do - time you dont have when
the enemy comes rushing at you. Crafting that armour will not be immediate
either. For some types of games the very act of moving or changing pose all
comes with a certain time associated with it.</p>
<p>There are two main suitable ways to introduce a delay in a <a class="reference internal" href="../Components/Commands.html"><span class="doc std std-doc">Command</span></a>s execution:</p>
<ul class="simple">
<li><p>Using <code class="docutils literal notranslate"><span class="pre">yield</span></code> in the Commands <code class="docutils literal notranslate"><span class="pre">func</span></code> method.</p></li>
<li><p>Using the <code class="docutils literal notranslate"><span class="pre">evennia.utils.delay</span></code> utility function.</p></li>
</ul>
<p>Well simplify both below.</p>
<section id="pause-commands-with-yield">
<h2>Pause commands with <code class="docutils literal notranslate"><span class="pre">yield</span></code><a class="headerlink" href="#pause-commands-with-yield" title="Permalink to this headline"></a></h2>
<p>The <code class="docutils literal notranslate"><span class="pre">yield</span></code> keyword is a reserved word in Python. Its used to create <a class="reference external" href="https://realpython.com/introduction-to-python-generators/">generators</a>, which are interesting in their own right. For the purpose of this howto though, we just need to know that Evennia will use it to pause the execution of the command for a certain time.</p>
<aside class="sidebar">
<p class="sidebar-title">This only works in Command.func!</p>
<p>This <code class="docutils literal notranslate"><span class="pre">yield</span></code> functionality will <em>only</em> work in the <code class="docutils literal notranslate"><span class="pre">func</span></code> method of
Commands. It works because Evennia has especially catered for it as a convenient shortcut. Trying to use it elsewhere will not work. If you want the same functionality elsewhere you should look up the <span class="xref myst">interactive decorator</span>.</p>
</aside>
<div class="highlight-python notranslate"><div class="highlight"><table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre><span class="normal"> 1</span>
<span class="normal"> 2</span>
<span class="normal"> 3</span>
<span class="normal"> 4</span>
<span class="normal"> 5</span>
<span class="normal"> 6</span>
<span class="normal"> 7</span>
<span class="normal"> 8</span>
<span class="normal"> 9</span>
<span class="normal">10</span>
<span class="normal">11</span>
<span class="normal">12</span>
<span class="normal">13</span>
<span class="normal">14</span>
<span class="normal">15</span>
<span class="normal">16</span></pre></div></td><td class="code"><div><pre><span></span><span class="k">class</span> <span class="nc">CmdTest</span><span class="p">(</span><span class="n">Command</span><span class="p">):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;</span>
<span class="sd"> A test command just to test waiting.</span>
<span class="sd"> Usage:</span>
<span class="sd"> test</span>
<span class="sd"> &quot;&quot;&quot;</span>
<span class="n">key</span> <span class="o">=</span> <span class="s2">&quot;test&quot;</span>
<span class="k">def</span> <span class="nf">func</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="bp">self</span><span class="o">.</span><span class="n">msg</span><span class="p">(</span><span class="s2">&quot;Before ten seconds...&quot;</span><span class="p">)</span>
<span class="hll"> <span class="k">yield</span> <span class="mi">10</span>
</span> <span class="bp">self</span><span class="o">.</span><span class="n">msg</span><span class="p">(</span><span class="s2">&quot;Afterwards.&quot;</span><span class="p">)</span>
</pre></div></td></tr></table></div>
</div>
<ul class="simple">
<li><p><strong>Line 15</strong> : This is the important line. The <code class="docutils literal notranslate"><span class="pre">yield</span> <span class="pre">10</span></code> tells Evennia to “pause” the command
and to wait for 10 seconds to execute the rest. If you add this command and
run it, youll see the first message, then, after a pause of ten seconds, the
next message. You can use <code class="docutils literal notranslate"><span class="pre">yield</span></code> several times in your command.</p></li>
</ul>
<p>This syntax will not “freeze” all commands. While the command is “pausing”, you can execute other commands (or even call the same command again). And other players arent frozen either.</p>
<blockquote>
<div><p>Using <code class="docutils literal notranslate"><span class="pre">yield</span></code> is non-persistent. If you <code class="docutils literal notranslate"><span class="pre">reload</span></code> the game while a command is “paused”, that pause state is lost and it will <em>not</em> resume after the server has reloaded.</p>
</div></blockquote>
</section>
<section id="pause-commands-with-utils-delay">
<h2>Pause commands with <code class="docutils literal notranslate"><span class="pre">utils.delay</span></code><a class="headerlink" href="#pause-commands-with-utils-delay" title="Permalink to this headline"></a></h2>
<p>The <code class="docutils literal notranslate"><span class="pre">yield</span></code> syntax is easy to read, easy to understand, easy to use. But its non-persistent and not that flexible if you want more advanced options.</p>
<p>The <code class="docutils literal notranslate"><span class="pre">evennia.utils.delay</span></code> represents is a more powerful way to introduce delays. Unlike <code class="docutils literal notranslate"><span class="pre">yield</span></code>, it<br />
can be made persistent and also works outside of <code class="docutils literal notranslate"><span class="pre">Command.func</span></code>. Its however a little more cumbersome to write since unlike <code class="docutils literal notranslate"><span class="pre">yield</span></code> it will not actually stop at the line its called.</p>
<div class="highlight-python notranslate"><div class="highlight"><table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre><span class="normal"> 1</span>
<span class="normal"> 2</span>
<span class="normal"> 3</span>
<span class="normal"> 4</span>
<span class="normal"> 5</span>
<span class="normal"> 6</span>
<span class="normal"> 7</span>
<span class="normal"> 8</span>
<span class="normal"> 9</span>
<span class="normal">10</span>
<span class="normal">11</span>
<span class="normal">12</span>
<span class="normal">13</span>
<span class="normal">14</span>
<span class="normal">15</span>
<span class="normal">16</span>
<span class="normal">17</span>
<span class="normal">18</span>
<span class="normal">19</span>
<span class="normal">20</span>
<span class="normal">21</span>
<span class="normal">22</span>
<span class="normal">23</span>
<span class="normal">24</span>
<span class="normal">25</span>
<span class="normal">26</span>
<span class="normal">27</span>
<span class="normal">28</span>
<span class="normal">29</span>
<span class="normal">30</span>
<span class="normal">31</span></pre></div></td><td class="code"><div><pre><span></span><span class="kn">from</span> <span class="nn">evennia</span> <span class="kn">import</span> <span class="n">default_cmds</span><span class="p">,</span> <span class="n">utils</span>
<span class="k">class</span> <span class="nc">CmdEcho</span><span class="p">(</span><span class="n">default_cmds</span><span class="o">.</span><span class="n">MuxCommand</span><span class="p">):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;</span>
<span class="sd"> Wait for an echo</span>
<span class="sd"> </span>
<span class="sd"> Usage: </span>
<span class="sd"> echo &lt;string&gt;</span>
<span class="sd"> </span>
<span class="sd"> Calls and waits for an echo.</span>
<span class="sd"> &quot;&quot;&quot;</span>
<span class="n">key</span> <span class="o">=</span> <span class="s2">&quot;echo&quot;</span>
<span class="hll"> <span class="k">def</span> <span class="nf">echo</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
</span> <span class="s2">&quot;Called after 10 seconds.&quot;</span>
<span class="n">shout</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">args</span>
<span class="bp">self</span><span class="o">.</span><span class="n">caller</span><span class="o">.</span><span class="n">msg</span><span class="p">(</span>
<span class="s2">&quot;You hear an echo: &quot;</span>
<span class="sa">f</span><span class="s2">&quot;</span><span class="si">{</span><span class="n">shout</span><span class="o">.</span><span class="n">upper</span><span class="p">()</span><span class="si">}</span><span class="s2"> ... &quot;</span>
<span class="sa">f</span><span class="s2">&quot;</span><span class="si">{</span><span class="n">shout</span><span class="o">.</span><span class="n">capitalize</span><span class="p">()</span><span class="si">}</span><span class="s2"> ... &quot;</span>
<span class="sa">f</span><span class="s2">&quot;</span><span class="si">{</span><span class="n">shout</span><span class="o">.</span><span class="n">lower</span><span class="p">()</span><span class="si">}</span><span class="s2">&quot;</span>
<span class="p">)</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="w"> </span><span class="sd">&quot;&quot;&quot;</span>
<span class="sd"> This is called at the initial shout. </span>
<span class="sd"> &quot;&quot;&quot;</span>
<span class="bp">self</span><span class="o">.</span><span class="n">caller</span><span class="o">.</span><span class="n">msg</span><span class="p">(</span><span class="sa">f</span><span class="s2">&quot;You shout &#39;</span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">args</span><span class="si">}</span><span class="s2">&#39; and wait for an echo ...&quot;</span><span class="p">)</span>
<span class="c1"># this waits non-blocking for 10 seconds, then calls self.echo</span>
<span class="hll"> <span class="n">utils</span><span class="o">.</span><span class="n">delay</span><span class="p">(</span><span class="mi">10</span><span class="p">,</span> <span class="bp">self</span><span class="o">.</span><span class="n">echo</span><span class="p">)</span> <span class="c1"># call echo after 10 seconds</span>
</span>
</pre></div></td></tr></table></div>
</div>
<p>Import this new echo command into the default command set and reload the server. You will find that it will take 10 seconds before you see your shout coming back.</p>
<ul class="simple">
<li><p><strong>Line 14</strong>: We add a new method <code class="docutils literal notranslate"><span class="pre">echo</span></code>. This is a <em>callback</em> - a method/function we will call after a certain time.</p></li>
<li><p><strong>Line 30</strong>: Here we use <code class="docutils literal notranslate"><span class="pre">utils.delay</span></code> to tell Evennia “Please wait for 10 seconds, then call “<code class="docutils literal notranslate"><span class="pre">self.echo</span></code>”. Note how we pass <code class="docutils literal notranslate"><span class="pre">self.echo</span></code> and <em>not</em> <code class="docutils literal notranslate"><span class="pre">self.echo()</span></code>! If we did the latter, <code class="docutils literal notranslate"><span class="pre">echo</span></code> would fire <em>immediately</em>. Instead we let Evennia do this call for us ten seconds later.</p></li>
</ul>
<p>You will also find that this is a <em>non-blocking</em> effect; you can issue other commands in the interim and the game will go on as usual. The echo will come back to you in its own time.</p>
<p>The call signature for <code class="docutils literal notranslate"><span class="pre">utils.delay</span></code> is:</p>
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="n">utils</span><span class="o">.</span><span class="n">delay</span><span class="p">(</span><span class="n">timedelay</span><span class="p">,</span> <span class="n">callback</span><span class="p">,</span> <span class="n">persistent</span><span class="o">=</span><span class="kc">False</span><span class="p">,</span> <span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">)</span>
</pre></div>
</div>
<aside class="sidebar">
<p class="sidebar-title">*args and **kwargs </p>
<p>These are used to indicate any number of arguments or keyword-arguments should be picked up here. In code they are treated as a <code class="docutils literal notranslate"><span class="pre">tuple</span></code> and a <code class="docutils literal notranslate"><span class="pre">dict</span></code> respectively.</p>
<p><code class="docutils literal notranslate"><span class="pre">*args</span></code> and <code class="docutils literal notranslate"><span class="pre">**kwargs</span></code> are used in many places in Evennia. <a class="reference external" href="https://realpython.com/python-kwargs-and-args">See an online tutorial here</a>.</p>
</aside>
<p>If you set <code class="docutils literal notranslate"><span class="pre">persistent=True</span></code>, this delay will survive a <code class="docutils literal notranslate"><span class="pre">reload</span></code>. If you pass <code class="docutils literal notranslate"><span class="pre">*args</span></code> and/or <code class="docutils literal notranslate"><span class="pre">**kwargs</span></code>, they will be passed on into the <code class="docutils literal notranslate"><span class="pre">callback</span></code>. So this way you can pass more complex arguments to the delayed function.</p>
<p>Its important to remember that the <code class="docutils literal notranslate"><span class="pre">delay()</span></code> call will not “pause” at that point when it is
called (the way <code class="docutils literal notranslate"><span class="pre">yield</span></code> does in the previous section). The lines after the <code class="docutils literal notranslate"><span class="pre">delay()</span></code> call will
actually execute <em>right away</em>. What you must do is to tell it which function to call <em>after the time
has passed</em> (its “callback”). This may sound strange at first, but it is normal practice in
asynchronous systems. You can also link such calls together:</p>
<div class="highlight-default notranslate"><div class="highlight"><table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre><span class="normal"> 1</span>
<span class="normal"> 2</span>
<span class="normal"> 3</span>
<span class="normal"> 4</span>
<span class="normal"> 5</span>
<span class="normal"> 6</span>
<span class="normal"> 7</span>
<span class="normal"> 8</span>
<span class="normal"> 9</span>
<span class="normal">10</span>
<span class="normal">11</span>
<span class="normal">12</span>
<span class="normal">13</span>
<span class="normal">14</span>
<span class="normal">15</span>
<span class="normal">16</span>
<span class="normal">17</span>
<span class="normal">18</span>
<span class="normal">19</span>
<span class="normal">20</span>
<span class="normal">21</span>
<span class="normal">22</span>
<span class="normal">23</span>
<span class="normal">24</span>
<span class="normal">25</span>
<span class="normal">26</span>
<span class="normal">27</span>
<span class="normal">28</span>
<span class="normal">29</span>
<span class="normal">30</span>
<span class="normal">31</span>
<span class="normal">32</span>
<span class="normal">33</span>
<span class="normal">34</span>
<span class="normal">35</span>
<span class="normal">36</span></pre></div></td><td class="code"><div><pre><span></span><span class="kn">from</span> <span class="nn">evennia</span> <span class="kn">import</span> <span class="n">default_cmds</span><span class="p">,</span> <span class="n">utils</span>
<span class="k">class</span> <span class="nc">CmdEcho</span><span class="p">(</span><span class="n">default_cmds</span><span class="o">.</span><span class="n">MuxCommand</span><span class="p">):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;</span>
<span class="sd"> waits for an echo</span>
<span class="sd"> </span>
<span class="sd"> Usage: </span>
<span class="sd"> echo &lt;string&gt;</span>
<span class="sd"> </span>
<span class="sd"> Calls and waits for an echo</span>
<span class="sd"> &quot;&quot;&quot;</span>
<span class="n">key</span> <span class="o">=</span> <span class="s2">&quot;echo&quot;</span>
<span class="k">def</span> <span class="nf">func</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="s2">&quot;This sets off a chain of delayed calls&quot;</span>
<span class="bp">self</span><span class="o">.</span><span class="n">caller</span><span class="o">.</span><span class="n">msg</span><span class="p">(</span><span class="sa">f</span><span class="s2">&quot;You shout &#39;</span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">args</span><span class="si">}</span><span class="s2">&#39;, waiting for an echo ...&quot;</span><span class="p">)</span>
<span class="c1"># wait 2 seconds before calling self.echo1</span>
<span class="hll"> <span class="n">utils</span><span class="o">.</span><span class="n">delay</span><span class="p">(</span><span class="mi">2</span><span class="p">,</span> <span class="bp">self</span><span class="o">.</span><span class="n">echo1</span><span class="p">)</span>
</span>
<span class="c1"># callback chain, started above</span>
<span class="hll"> <span class="k">def</span> <span class="nf">echo1</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
</span> <span class="s2">&quot;First echo&quot;</span>
<span class="bp">self</span><span class="o">.</span><span class="n">caller</span><span class="o">.</span><span class="n">msg</span><span class="p">(</span><span class="sa">f</span><span class="s2">&quot;... </span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">args</span><span class="o">.</span><span class="n">upper</span><span class="p">()</span><span class="si">}</span><span class="s2">&quot;</span><span class="p">)</span>
<span class="c1"># wait 2 seconds for the next one</span>
<span class="n">utils</span><span class="o">.</span><span class="n">delay</span><span class="p">(</span><span class="mi">2</span><span class="p">,</span> <span class="bp">self</span><span class="o">.</span><span class="n">echo2</span><span class="p">)</span>
<span class="hll"> <span class="k">def</span> <span class="nf">echo2</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
</span> <span class="s2">&quot;Second echo&quot;</span>
<span class="bp">self</span><span class="o">.</span><span class="n">caller</span><span class="o">.</span><span class="n">msg</span><span class="p">(</span><span class="sa">f</span><span class="s2">&quot;... </span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">args</span><span class="o">.</span><span class="n">capitalize</span><span class="p">()</span><span class="si">}</span><span class="s2">&quot;</span><span class="p">)</span>
<span class="c1"># wait another 2 seconds</span>
<span class="n">utils</span><span class="o">.</span><span class="n">delay</span><span class="p">(</span><span class="mi">2</span><span class="p">,</span> <span class="n">callback</span><span class="o">=</span><span class="bp">self</span><span class="o">.</span><span class="n">echo3</span><span class="p">)</span>
<span class="hll"> <span class="k">def</span> <span class="nf">echo3</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
</span> <span class="s2">&quot;Last echo&quot;</span>
<span class="bp">self</span><span class="o">.</span><span class="n">caller</span><span class="o">.</span><span class="n">msg</span><span class="p">(</span><span class="sa">f</span><span class="s2">&quot;... </span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">args</span><span class="o">.</span><span class="n">lower</span><span class="p">()</span><span class="si">}</span><span class="s2"> ...&quot;</span><span class="p">)</span>
</pre></div></td></tr></table></div>
</div>
<p>The above version will have the echoes arrive one after another, each separated by a two second
delay.</p>
<ul class="simple">
<li><p><strong>Line 19</strong>: This sets off the chain, telling Evennia to wait 2 seconds before calling <code class="docutils literal notranslate"><span class="pre">self.echo1</span></code>.</p></li>
<li><p><strong>Line 22</strong>: This is called after 2 seconds. It tells Evennia to wait another 2 seconds before calling <code class="docutils literal notranslate"><span class="pre">self.echo2</span></code>.</p></li>
<li><p><strong>Line 28</strong>: This is called after yet another 2 seonds (4s total). It tells Evennia to wait another 2 seconds before calling, <code class="docutils literal notranslate"><span class="pre">self.echo3</span></code>.</p></li>
<li><p><strong>Line34</strong> Called after another 2 seconds (6s total). This ends the delay-chain.</p></li>
</ul>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span>&gt; echo Hello!
... HELLO!
... Hello!
... hello! ...
</pre></div>
</div>
<div class="admonition warning">
<p class="admonition-title">Warning</p>
<p>What about time.sleep?</p>
<p>You may be aware of the <code class="docutils literal notranslate"><span class="pre">time.sleep</span></code> function coming with Python. Doing `time.sleep(10) pauses Python for 10 seconds. <strong>Do not use this</strong>, it will not work with Evennia. If you use it, you will block the <em>entire server</em> (everyone!) for ten seconds!</p>
<p>If you want specifics, <code class="docutils literal notranslate"><span class="pre">utils.delay</span></code> is a thin wrapper around a <a class="reference external" href="https://docs.twisted.org/en/twisted-22.1.0/core/howto/defer.html">Twisted Deferred</a>. This is an <a class="reference internal" href="../Concepts/Async-Process.html"><span class="doc std std-doc">asynchronous concept</span></a>.</p>
</div>
</section>
<section id="making-a-blocking-command">
<h2>Making a blocking command<a class="headerlink" href="#making-a-blocking-command" title="Permalink to this headline"></a></h2>
<p>Both <code class="docutils literal notranslate"><span class="pre">yield</span></code> or <code class="docutils literal notranslate"><span class="pre">utils.delay()</span></code> pauses the command but allows the user to use other commands while the first one waits to finish.</p>
<p>In some cases you want to instead have that command block other commands from running. An example is crafting a helmet: most likely you should not be able to start crafting a shield at the same time. Or even walk out of the smithy.</p>
<p>The simplest way of implementing blocking is to use the technique covered in the <a class="reference internal" href="Howto-Command-Cooldown.html"><span class="doc std std-doc">How to implement a Command Cooldown</span></a> tutorial. In that tutorial we cooldowns are implemented by comparing the current time with the last time the command was used. This is the best approach if you can get away with it. It could work well for our crafting example … <em>if</em> you dont want to automatically update the player on their progress.</p>
<p>In short:
- If you are fine with the player making an active input to check their status, compare timestamps as done in the Command-cooldown tutorial. On-demand is by far the most efficent.
- If you want Evennia to tell the user their status without them taking a further action, you need to use <code class="docutils literal notranslate"><span class="pre">yield</span></code> , <code class="docutils literal notranslate"><span class="pre">delay</span></code> (or some other active time-keeping method).</p>
<p>Here is an example where we will use <code class="docutils literal notranslate"><span class="pre">utils.delay</span></code> to tell the player when the cooldown has passed:</p>
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="kn">from</span> <span class="nn">evennia</span> <span class="kn">import</span> <span class="n">utils</span><span class="p">,</span> <span class="n">default_cmds</span>
<span class="k">class</span> <span class="nc">CmdBigSwing</span><span class="p">(</span><span class="n">default_cmds</span><span class="o">.</span><span class="n">MuxCommand</span><span class="p">):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;</span>
<span class="sd"> swing your weapon in a big way</span>
<span class="sd"> Usage:</span>
<span class="sd"> swing &lt;target&gt;</span>
<span class="sd"> </span>
<span class="sd"> Makes a mighty swing. Doing so will make you vulnerable</span>
<span class="sd"> to counter-attacks before you can recover. </span>
<span class="sd"> &quot;&quot;&quot;</span>
<span class="n">key</span> <span class="o">=</span> <span class="s2">&quot;bigswing&quot;</span>
<span class="n">locks</span> <span class="o">=</span> <span class="s2">&quot;cmd:all()&quot;</span>
<span class="k">def</span> <span class="nf">func</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="s2">&quot;Makes the swing&quot;</span>
<span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">caller</span><span class="o">.</span><span class="n">ndb</span><span class="o">.</span><span class="n">off_balance</span><span class="p">:</span>
<span class="c1"># we are still off-balance.</span>
<span class="bp">self</span><span class="o">.</span><span class="n">caller</span><span class="o">.</span><span class="n">msg</span><span class="p">(</span><span class="s2">&quot;You are off balance and need time to recover!&quot;</span><span class="p">)</span>
<span class="k">return</span>
<span class="c1"># [attack/hit code goes here ...]</span>
<span class="bp">self</span><span class="o">.</span><span class="n">caller</span><span class="o">.</span><span class="n">msg</span><span class="p">(</span><span class="s2">&quot;You swing big! You are off balance now.&quot;</span><span class="p">)</span>
<span class="c1"># set the off-balance flag</span>
<span class="bp">self</span><span class="o">.</span><span class="n">caller</span><span class="o">.</span><span class="n">ndb</span><span class="o">.</span><span class="n">off_balance</span> <span class="o">=</span> <span class="kc">True</span>
<span class="c1"># wait 8 seconds before we can recover. During this time </span>
<span class="c1"># we won&#39;t be able to swing again due to the check at the top. </span>
<span class="n">utils</span><span class="o">.</span><span class="n">delay</span><span class="p">(</span><span class="mi">8</span><span class="p">,</span> <span class="bp">self</span><span class="o">.</span><span class="n">recover</span><span class="p">)</span>
<span class="k">def</span> <span class="nf">recover</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="s2">&quot;This will be called after 8 secs&quot;</span>
<span class="k">del</span> <span class="bp">self</span><span class="o">.</span><span class="n">caller</span><span class="o">.</span><span class="n">ndb</span><span class="o">.</span><span class="n">off_balance</span>
<span class="bp">self</span><span class="o">.</span><span class="n">caller</span><span class="o">.</span><span class="n">msg</span><span class="p">(</span><span class="s2">&quot;You regain your balance.&quot;</span><span class="p">)</span>
</pre></div>
</div>
<p>Note how, after the cooldown, the user will get a message telling them they are now ready for
another swing.</p>
<p>By storing the <code class="docutils literal notranslate"><span class="pre">off_balance</span></code> flag on the character (rather than on, say, the Command instance
itself) it can be accessed by other Commands too. Other attacks may also not work when you are off balance. You could also have an enemy Command check your <code class="docutils literal notranslate"><span class="pre">off_balance</span></code> status to gain bonuses, to take another example.</p>
</section>
<section id="make-a-command-possible-to-abort">
<h2>Make a Command possible to Abort<a class="headerlink" href="#make-a-command-possible-to-abort" title="Permalink to this headline"></a></h2>
<p>One can imagine that you will want to abort a long-running command before it has a time to finish.
If you are in the middle of crafting your armor you will probably want to stop doing that when a
monster enters your smithy.</p>
<p>You can implement this in the same way as you do the “blocking” command above, just in reverse.
Below is an example of a crafting command that can be aborted by starting a fight:</p>
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="kn">from</span> <span class="nn">evennia</span> <span class="kn">import</span> <span class="n">utils</span><span class="p">,</span> <span class="n">default_cmds</span>
<span class="k">class</span> <span class="nc">CmdCraftArmour</span><span class="p">(</span><span class="n">default_cmds</span><span class="o">.</span><span class="n">MuxCommand</span><span class="p">):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;</span>
<span class="sd"> Craft armour</span>
<span class="sd"> </span>
<span class="sd"> Usage:</span>
<span class="sd"> craft &lt;name of armour&gt;</span>
<span class="sd"> </span>
<span class="sd"> This will craft a suit of armour, assuming you</span>
<span class="sd"> have all the components and tools. Doing some</span>
<span class="sd"> other action (such as attacking someone) will </span>
<span class="sd"> abort the crafting process. </span>
<span class="sd"> &quot;&quot;&quot;</span>
<span class="n">key</span> <span class="o">=</span> <span class="s2">&quot;craft&quot;</span>
<span class="n">locks</span> <span class="o">=</span> <span class="s2">&quot;cmd:all()&quot;</span>
<span class="k">def</span> <span class="nf">func</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="s2">&quot;starts crafting&quot;</span>
<span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">caller</span><span class="o">.</span><span class="n">ndb</span><span class="o">.</span><span class="n">is_crafting</span><span class="p">:</span>
<span class="bp">self</span><span class="o">.</span><span class="n">caller</span><span class="o">.</span><span class="n">msg</span><span class="p">(</span><span class="s2">&quot;You are already crafting!&quot;</span><span class="p">)</span>
<span class="k">return</span>
<span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">_is_fighting</span><span class="p">():</span>
<span class="bp">self</span><span class="o">.</span><span class="n">caller</span><span class="o">.</span><span class="n">msg</span><span class="p">(</span><span class="s2">&quot;You can&#39;t start to craft &quot;</span>
<span class="s2">&quot;in the middle of a fight!&quot;</span><span class="p">)</span>
<span class="k">return</span>
<span class="c1"># [Crafting code, checking of components, skills etc] </span>
<span class="c1"># Start crafting</span>
<span class="bp">self</span><span class="o">.</span><span class="n">caller</span><span class="o">.</span><span class="n">ndb</span><span class="o">.</span><span class="n">is_crafting</span> <span class="o">=</span> <span class="kc">True</span>
<span class="bp">self</span><span class="o">.</span><span class="n">caller</span><span class="o">.</span><span class="n">msg</span><span class="p">(</span><span class="s2">&quot;You start crafting ...&quot;</span><span class="p">)</span>
<span class="n">utils</span><span class="o">.</span><span class="n">delay</span><span class="p">(</span><span class="mi">60</span><span class="p">,</span> <span class="bp">self</span><span class="o">.</span><span class="n">step1</span><span class="p">)</span>
<span class="k">def</span> <span class="nf">_is_fighting</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="s2">&quot;checks if we are in a fight.&quot;</span>
<span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">caller</span><span class="o">.</span><span class="n">ndb</span><span class="o">.</span><span class="n">is_fighting</span><span class="p">:</span>
<span class="k">del</span> <span class="bp">self</span><span class="o">.</span><span class="n">caller</span><span class="o">.</span><span class="n">ndb</span><span class="o">.</span><span class="n">is_crafting</span>
<span class="k">return</span> <span class="kc">True</span>
<span class="k">def</span> <span class="nf">step1</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="s2">&quot;first step of armour construction&quot;</span>
<span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">_is_fighting</span><span class="p">():</span>
<span class="k">return</span>
<span class="bp">self</span><span class="o">.</span><span class="n">msg</span><span class="p">(</span><span class="s2">&quot;You create the first part of the armour.&quot;</span><span class="p">)</span>
<span class="n">utils</span><span class="o">.</span><span class="n">delay</span><span class="p">(</span><span class="mi">60</span><span class="p">,</span> <span class="n">callback</span><span class="o">=</span><span class="bp">self</span><span class="o">.</span><span class="n">step2</span><span class="p">)</span>
<span class="k">def</span> <span class="nf">step2</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="s2">&quot;second step of armour construction&quot;</span>
<span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">_is_fighting</span><span class="p">():</span>
<span class="k">return</span>
<span class="bp">self</span><span class="o">.</span><span class="n">msg</span><span class="p">(</span><span class="s2">&quot;You create the second part of the armour.&quot;</span><span class="p">)</span>
<span class="n">utils</span><span class="o">.</span><span class="n">delay</span><span class="p">(</span><span class="mi">60</span><span class="p">,</span> <span class="n">step3</span><span class="p">)</span>
<span class="k">def</span> <span class="nf">step3</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="s2">&quot;last step of armour construction&quot;</span>
<span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">_is_fighting</span><span class="p">():</span>
<span class="k">return</span>
<span class="c1"># [code for creating the armour object etc]</span>
<span class="k">del</span> <span class="bp">self</span><span class="o">.</span><span class="n">caller</span><span class="o">.</span><span class="n">ndb</span><span class="o">.</span><span class="n">is_crafting</span>
<span class="bp">self</span><span class="o">.</span><span class="n">msg</span><span class="p">(</span><span class="s2">&quot;You finalize your armour.&quot;</span><span class="p">)</span>
<span class="c1"># example of a command that aborts crafting</span>
<span class="k">class</span> <span class="nc">CmdAttack</span><span class="p">(</span><span class="n">default_cmds</span><span class="o">.</span><span class="n">MuxCommand</span><span class="p">):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;</span>
<span class="sd"> attack someone</span>
<span class="sd"> </span>
<span class="sd"> Usage:</span>
<span class="sd"> attack &lt;target&gt;</span>
<span class="sd"> </span>
<span class="sd"> Try to cause harm to someone. This will abort</span>
<span class="sd"> eventual crafting you may be currently doing. </span>
<span class="sd"> &quot;&quot;&quot;</span>
<span class="n">key</span> <span class="o">=</span> <span class="s2">&quot;attack&quot;</span>
<span class="n">aliases</span> <span class="o">=</span> <span class="p">[</span><span class="s2">&quot;hit&quot;</span><span class="p">,</span> <span class="s2">&quot;stab&quot;</span><span class="p">]</span>
<span class="n">locks</span> <span class="o">=</span> <span class="s2">&quot;cmd:all()&quot;</span>
<span class="k">def</span> <span class="nf">func</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="s2">&quot;Implements the command&quot;</span>
<span class="bp">self</span><span class="o">.</span><span class="n">caller</span><span class="o">.</span><span class="n">ndb</span><span class="o">.</span><span class="n">is_fighting</span> <span class="o">=</span> <span class="kc">True</span>
<span class="c1"># [...]</span>
</pre></div>
</div>
<p>The above code creates a delayed crafting command that will gradually create the armour. If the
<code class="docutils literal notranslate"><span class="pre">attack</span></code> command is issued during this process it will set a flag that causes the crafting to be
quietly canceled next time it tries to update.</p>
</section>
</section>
</div>
</div>
</div>
</div>
<div class="related" role="navigation" aria-label="related navigation">
<h3>Navigation</h3>
<ul>
<li class="right" style="margin-right: 10px">
<a href="../genindex.html" title="General Index"
>index</a></li>
<li class="right" >
<a href="../py-modindex.html" title="Python Module Index"
>modules</a> |</li>
<li class="right" >
<a href="Howto-Default-Exit-Errors.html" title="Return custom errors on missing Exits"
>next</a> |</li>
<li class="right" >
<a href="Howto-Command-Cooldown.html" title="Adding Command Cooldowns"
>previous</a> |</li>
<li class="nav-item nav-item-0"><a href="../index.html">Evennia latest</a> &#187;</li>
<li class="nav-item nav-item-1"><a href="Howtos-Overview.html" >Tutorials and How-Tos</a> &#187;</li>
<li class="nav-item nav-item-this"><a href="">Commands that take time to finish</a></li>
</ul>
</div>
<div class="footer" role="contentinfo">
&#169; Copyright 2024, The Evennia developer community.
Created using <a href="https://www.sphinx-doc.org/">Sphinx</a> 3.2.1.
</div>
</body>
</html>