<p>There are two main suitable ways to introduce a ‘delay’ in a <aclass="reference internal"href="../Components/Commands.html"><spanclass="doc std std-doc">Command</span></a>’s execution:</p>
<ulclass="simple">
<li><p>Using <codeclass="docutils literal notranslate"><spanclass="pre">yield</span></code> in the Command’s <codeclass="docutils literal notranslate"><spanclass="pre">func</span></code> method.</p></li>
<li><p>Using the <codeclass="docutils literal notranslate"><spanclass="pre">evennia.utils.delay</span></code> utility function.</p></li>
</ul>
<p>We’ll simplify both below.</p>
<sectionid="pause-commands-with-yield">
<h2>Pause commands with <codeclass="docutils literal notranslate"><spanclass="pre">yield</span></code><aclass="headerlink"href="#pause-commands-with-yield"title="Permalink to this headline">¶</a></h2>
<p>The <codeclass="docutils literal notranslate"><spanclass="pre">yield</span></code> keyword is a reserved word in Python. It’s used to create <aclass="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>
<asideclass="sidebar">
<pclass="sidebar-title">This only works in Command.func!</p>
<p>This <codeclass="docutils literal notranslate"><spanclass="pre">yield</span></code> functionality will <em>only</em> work in the <codeclass="docutils literal notranslate"><spanclass="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 <spanclass="xref myst">interactive decorator</span>.</p>
<spanclass="bp">self</span><spanclass="o">.</span><spanclass="n">msg</span><spanclass="p">(</span><spanclass="s2">"Before ten seconds..."</span><spanclass="p">)</span>
<li><p><strong>Line 15</strong> : This is the important line. The <codeclass="docutils literal notranslate"><spanclass="pre">yield</span><spanclass="pre">10</span></code> tells Evennia to “pause” the command
next message. You can use <codeclass="docutils literal notranslate"><spanclass="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 aren’t frozen either.</p>
<div><p>Using <codeclass="docutils literal notranslate"><spanclass="pre">yield</span></code> is non-persistent. If you <codeclass="docutils literal notranslate"><spanclass="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>
<h2>Pause commands with <codeclass="docutils literal notranslate"><spanclass="pre">utils.delay</span></code><aclass="headerlink"href="#pause-commands-with-utils-delay"title="Permalink to this headline">¶</a></h2>
<p>The <codeclass="docutils literal notranslate"><spanclass="pre">yield</span></code> syntax is easy to read, easy to understand, easy to use. But it’s non-persistent and not that flexible if you want more advanced options.</p>
<p>The <codeclass="docutils literal notranslate"><spanclass="pre">evennia.utils.delay</span></code> represents is a more powerful way to introduce delays. Unlike <codeclass="docutils literal notranslate"><spanclass="pre">yield</span></code>, it<br/>
can be made persistent and also works outside of <codeclass="docutils literal notranslate"><spanclass="pre">Command.func</span></code>. It’s however a little more cumbersome to write since unlike <codeclass="docutils literal notranslate"><spanclass="pre">yield</span></code> it will not actually stop at the line it’s called.</p>
<spanclass="sd"> This is called at the initial shout. </span>
<spanclass="sd">"""</span>
<spanclass="bp">self</span><spanclass="o">.</span><spanclass="n">caller</span><spanclass="o">.</span><spanclass="n">msg</span><spanclass="p">(</span><spanclass="sa">f</span><spanclass="s2">"You shout '</span><spanclass="si">{</span><spanclass="bp">self</span><spanclass="o">.</span><spanclass="n">args</span><spanclass="si">}</span><spanclass="s2">' and wait for an echo ..."</span><spanclass="p">)</span>
<spanclass="c1"># this waits non-blocking for 10 seconds, then calls self.echo</span>
<spanclass="hll"><spanclass="n">utils</span><spanclass="o">.</span><spanclass="n">delay</span><spanclass="p">(</span><spanclass="mi">10</span><spanclass="p">,</span><spanclass="bp">self</span><spanclass="o">.</span><spanclass="n">echo</span><spanclass="p">)</span><spanclass="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>
<ulclass="simple">
<li><p><strong>Line 14</strong>: We add a new method <codeclass="docutils literal notranslate"><spanclass="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 <codeclass="docutils literal notranslate"><spanclass="pre">utils.delay</span></code> to tell Evennia “Please wait for 10 seconds, then call “<codeclass="docutils literal notranslate"><spanclass="pre">self.echo</span></code>”. Note how we pass <codeclass="docutils literal notranslate"><spanclass="pre">self.echo</span></code> and <em>not</em><codeclass="docutils literal notranslate"><spanclass="pre">self.echo()</span></code>! If we did the latter, <codeclass="docutils literal notranslate"><spanclass="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 <codeclass="docutils literal notranslate"><spanclass="pre">utils.delay</span></code> is:</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 <codeclass="docutils literal notranslate"><spanclass="pre">tuple</span></code> and a <codeclass="docutils literal notranslate"><spanclass="pre">dict</span></code> respectively.</p>
<p><codeclass="docutils literal notranslate"><spanclass="pre">*args</span></code> and <codeclass="docutils literal notranslate"><spanclass="pre">**kwargs</span></code> are used in many places in Evennia. <aclass="reference external"href="https://realpython.com/python-kwargs-and-args">See an online tutorial here</a>.</p>
</aside>
<p>If you set <codeclass="docutils literal notranslate"><spanclass="pre">persistent=True</span></code>, this delay will survive a <codeclass="docutils literal notranslate"><spanclass="pre">reload</span></code>. If you pass <codeclass="docutils literal notranslate"><spanclass="pre">*args</span></code> and/or <codeclass="docutils literal notranslate"><spanclass="pre">**kwargs</span></code>, they will be passed on into the <codeclass="docutils literal notranslate"><spanclass="pre">callback</span></code>. So this way you can pass more complex arguments to the delayed function.</p>
<p>It’s important to remember that the <codeclass="docutils literal notranslate"><spanclass="pre">delay()</span></code> call will not “pause” at that point when it is
called (the way <codeclass="docutils literal notranslate"><spanclass="pre">yield</span></code> does in the previous section). The lines after the <codeclass="docutils literal notranslate"><spanclass="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
<spanclass="s2">"This sets off a chain of delayed calls"</span>
<spanclass="bp">self</span><spanclass="o">.</span><spanclass="n">caller</span><spanclass="o">.</span><spanclass="n">msg</span><spanclass="p">(</span><spanclass="sa">f</span><spanclass="s2">"You shout '</span><spanclass="si">{</span><spanclass="bp">self</span><spanclass="o">.</span><spanclass="n">args</span><spanclass="si">}</span><spanclass="s2">', waiting for an echo ..."</span><spanclass="p">)</span>
<spanclass="c1"># wait 2 seconds before calling self.echo1</span>
<li><p><strong>Line 19</strong>: This sets off the chain, telling Evennia to wait 2 seconds before calling <codeclass="docutils literal notranslate"><spanclass="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 <codeclass="docutils literal notranslate"><spanclass="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, <codeclass="docutils literal notranslate"><spanclass="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>
<p>You may be aware of the <codeclass="docutils literal notranslate"><spanclass="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, <codeclass="docutils literal notranslate"><spanclass="pre">utils.delay</span></code> is a thin wrapper around a <aclass="reference external"href="https://docs.twisted.org/en/twisted-22.1.0/core/howto/defer.html">Twisted Deferred</a>. This is an <aclass="reference internal"href="../Concepts/Async-Process.html"><spanclass="doc std std-doc">asynchronous concept</span></a>.</p>
<h2>Making a blocking command<aclass="headerlink"href="#making-a-blocking-command"title="Permalink to this headline">¶</a></h2>
<p>Both <codeclass="docutils literal notranslate"><spanclass="pre">yield</span></code> or <codeclass="docutils literal notranslate"><spanclass="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 <aclass="reference internal"href="Howto-Command-Cooldown.html"><spanclass="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 don’t 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 <codeclass="docutils literal notranslate"><spanclass="pre">yield</span></code> , <codeclass="docutils literal notranslate"><spanclass="pre">delay</span></code> (or some other active time-keeping method).</p>
<p>Here is an example where we will use <codeclass="docutils literal notranslate"><spanclass="pre">utils.delay</span></code> to tell the player when the cooldown has passed:</p>
<spanclass="c1"># we are still off-balance.</span>
<spanclass="bp">self</span><spanclass="o">.</span><spanclass="n">caller</span><spanclass="o">.</span><spanclass="n">msg</span><spanclass="p">(</span><spanclass="s2">"You are off balance and need time to recover!"</span><spanclass="p">)</span>
<spanclass="k">return</span>
<spanclass="c1"># [attack/hit code goes here ...]</span>
<spanclass="bp">self</span><spanclass="o">.</span><spanclass="n">caller</span><spanclass="o">.</span><spanclass="n">msg</span><spanclass="p">(</span><spanclass="s2">"You swing big! You are off balance now."</span><spanclass="p">)</span>
<spanclass="bp">self</span><spanclass="o">.</span><spanclass="n">caller</span><spanclass="o">.</span><spanclass="n">msg</span><spanclass="p">(</span><spanclass="s2">"You regain your balance."</span><spanclass="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 <codeclass="docutils literal notranslate"><spanclass="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 <codeclass="docutils literal notranslate"><spanclass="pre">off_balance</span></code> status to gain bonuses, to take another example.</p>
<spanclass="bp">self</span><spanclass="o">.</span><spanclass="n">caller</span><spanclass="o">.</span><spanclass="n">msg</span><spanclass="p">(</span><spanclass="s2">"You are already crafting!"</span><spanclass="p">)</span>
<spanclass="bp">self</span><spanclass="o">.</span><spanclass="n">msg</span><spanclass="p">(</span><spanclass="s2">"You create the first part of the armour."</span><spanclass="p">)</span>
<spanclass="bp">self</span><spanclass="o">.</span><spanclass="n">msg</span><spanclass="p">(</span><spanclass="s2">"You create the second part of the armour."</span><spanclass="p">)</span>
<spanclass="bp">self</span><spanclass="o">.</span><spanclass="n">msg</span><spanclass="p">(</span><spanclass="s2">"You finalize your armour."</span><spanclass="p">)</span>
<spanclass="c1"># example of a command that aborts crafting</span>
<p>The above code creates a delayed crafting command that will gradually create the armour. If the
<codeclass="docutils literal notranslate"><spanclass="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>