mirror of
https://github.com/evennia/evennia.git
synced 2026-03-17 13:26:30 +01:00
228 lines
No EOL
20 KiB
HTML
228 lines
No EOL
20 KiB
HTML
|
||
<!DOCTYPE html>
|
||
|
||
<html>
|
||
<head>
|
||
<meta charset="utf-8" />
|
||
<meta name="viewport" content="width=device-width, initial-scale=1.0" /><meta name="generator" content="Docutils 0.17.1: http://docutils.sourceforge.net/" />
|
||
|
||
<title>TickerHandler — Evennia 3.x 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="Signals" href="Signals.html" />
|
||
<link rel="prev" title="MonitorHandler" href="MonitorHandler.html" />
|
||
</head><body>
|
||
|
||
|
||
<div class="admonition important">
|
||
<p class="first admonition-title">Note</p>
|
||
<p class="last">You are reading an old version of the Evennia documentation. <a href="https://www.evennia.com/docs/latest/index.html">The latest version is here</a></p>.
|
||
</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"
|
||
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="Signals.html" title="Signals"
|
||
accesskey="N">next</a> |</li>
|
||
<li class="right" >
|
||
<a href="MonitorHandler.html" title="MonitorHandler"
|
||
accesskey="P">previous</a> |</li>
|
||
<li class="nav-item nav-item-0"><a href="../index.html">Evennia 3.x</a> »</li>
|
||
<li class="nav-item nav-item-1"><a href="Components-Overview.html" accesskey="U">Core Components</a> »</li>
|
||
<li class="nav-item nav-item-this"><a href="">TickerHandler</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="#">TickerHandler</a><ul>
|
||
<li><a class="reference internal" href="#usage">Usage</a><ul>
|
||
<li><a class="reference internal" href="#when-not-to-use-tickerhandler">When <em>not</em> to use TickerHandler</a></li>
|
||
</ul>
|
||
</li>
|
||
</ul>
|
||
</li>
|
||
</ul>
|
||
|
||
<h4>Previous topic</h4>
|
||
<p class="topless"><a href="MonitorHandler.html"
|
||
title="previous chapter">MonitorHandler</a></p>
|
||
<h4>Next topic</h4>
|
||
<p class="topless"><a href="Signals.html"
|
||
title="next chapter">Signals</a></p>
|
||
<div role="note" aria-label="source link">
|
||
<!--h3>This Page</h3-->
|
||
<ul class="this-page-menu">
|
||
<li><a href="../_sources/Components/TickerHandler.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>
|
||
</div>
|
||
</div>
|
||
<div class="bodywrapper">
|
||
<div class="body" role="main">
|
||
|
||
<section class="tex2jax_ignore mathjax_ignore" id="tickerhandler">
|
||
<h1>TickerHandler<a class="headerlink" href="#tickerhandler" title="Permalink to this headline">¶</a></h1>
|
||
<p>One way to implement a dynamic MUD is by using “tickers”, also known as “heartbeats”. A ticker is a timer that fires (“ticks”) at a given interval. The tick triggers updates in various game systems.</p>
|
||
<p>Tickers are very common or even unavoidable in other mud code bases. Certain code bases are even hard-coded to rely on the concept of the global ‘tick’. Evennia has no such notion - the decision to use tickers is very much up to the need of your game and which requirements you have. The “ticker recipe” is just one way of cranking the wheels.</p>
|
||
<p>The most fine-grained way to manage the flow of time is to use <a class="reference internal" href="../api/evennia.utils.utils.html#evennia.utils.utils.delay" title="evennia.utils.utils.delay"><span class="xref myst py py-func">utils.delay</span></a> (using the <a class="reference internal" href="../api/evennia.scripts.taskhandler.html#evennia.scripts.taskhandler.TaskHandler" title="evennia.scripts.taskhandler.TaskHandler"><span class="xref myst py py-class">TaskHandler</span></a>). Another is to use the time-repeat capability of <a class="reference internal" href="Scripts.html"><span class="doc std std-doc">Scripts</span></a>. These tools operate on individual objects.</p>
|
||
<p>Many types of operations (weather being the classic example) are however done on multiple objects in the same way at regular intervals, and for this, it’s inefficient to set up separate delays/scripts for every such object.</p>
|
||
<p>The way to do this is to use a ticker with a “subscription model” - let objects sign up to be
|
||
triggered at the same interval, unsubscribing when the updating is no longer desired. This means that the time-keeping mechanism is only set up once for all objects, making subscribing/unsubscribing faster.</p>
|
||
<p>Evennia offers an optimized implementation of the subscription model - the <em>TickerHandler</em>. This is a singleton global handler reachable from <span class="xref myst">evennia.TICKER_HANDLER</span>. You can assign any <em>callable</em> (a function or, more commonly, a method on a database object) to this handler. The TickerHandler will then call this callable at an interval you specify, and with the arguments you supply when adding it. This continues until the callable un-subscribes from the ticker. The handler survives a reboot and is highly optimized in resource usage.</p>
|
||
<section id="usage">
|
||
<h2>Usage<a class="headerlink" href="#usage" title="Permalink to this headline">¶</a></h2>
|
||
<p>Here is an example of importing <code class="docutils literal notranslate"><span class="pre">TICKER_HANDLER</span></code> and using it:</p>
|
||
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span> <span class="c1"># we assume that obj has a hook "at_tick" defined on itself</span>
|
||
<span class="kn">from</span> <span class="nn">evennia</span> <span class="kn">import</span> <span class="n">TICKER_HANDLER</span> <span class="k">as</span> <span class="n">tickerhandler</span>
|
||
|
||
<span class="n">tickerhandler</span><span class="o">.</span><span class="n">add</span><span class="p">(</span><span class="mi">20</span><span class="p">,</span> <span class="n">obj</span><span class="o">.</span><span class="n">at_tick</span><span class="p">)</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>That’s it - from now on, <code class="docutils literal notranslate"><span class="pre">obj.at_tick()</span></code> will be called every 20 seconds.</p>
|
||
<div class="admonition important">
|
||
<p class="admonition-title">Important</p>
|
||
<p>Everything you supply to <code class="docutils literal notranslate"><span class="pre">TickerHandler.add</span></code> will need to be pickled at some point to be saved into the database - also if you use <code class="docutils literal notranslate"><span class="pre">persistent=False</span></code>. Most of the time the handler will correctly store things like database objects, but the same restrictions as for <a class="reference internal" href="Attributes.html"><span class="doc std std-doc">Attributes</span></a> apply to what the TickerHandler may store.</p>
|
||
</div>
|
||
<p>You can also import a function and tick that:</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">TICKER_HANDLER</span> <span class="k">as</span> <span class="n">tickerhandler</span>
|
||
<span class="kn">from</span> <span class="nn">mymodule</span> <span class="kn">import</span> <span class="n">myfunc</span>
|
||
|
||
<span class="n">tickerhandler</span><span class="o">.</span><span class="n">add</span><span class="p">(</span><span class="mi">30</span><span class="p">,</span> <span class="n">myfunc</span><span class="p">)</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>Removing (stopping) the ticker works as expected:</p>
|
||
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span> <span class="n">tickerhandler</span><span class="o">.</span><span class="n">remove</span><span class="p">(</span><span class="mi">20</span><span class="p">,</span> <span class="n">obj</span><span class="o">.</span><span class="n">at_tick</span><span class="p">)</span>
|
||
<span class="n">tickerhandler</span><span class="o">.</span><span class="n">remove</span><span class="p">(</span><span class="mi">30</span><span class="p">,</span> <span class="n">myfunc</span><span class="p">)</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>Note that you have to also supply <code class="docutils literal notranslate"><span class="pre">interval</span></code> to identify which subscription to remove. This is because the TickerHandler maintains a pool of tickers and a given callable can subscribe to be ticked at any number of different intervals.</p>
|
||
<p>The full definition of the <code class="docutils literal notranslate"><span class="pre">tickerhandler.add</span></code> method is</p>
|
||
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span> <span class="n">tickerhandler</span><span class="o">.</span><span class="n">add</span><span class="p">(</span><span class="n">interval</span><span class="p">,</span> <span class="n">callback</span><span class="p">,</span>
|
||
<span class="n">idstring</span><span class="o">=</span><span class="s2">""</span><span class="p">,</span> <span class="n">persistent</span><span class="o">=</span><span class="kc">True</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>
|
||
<p>Here <code class="docutils literal notranslate"><span class="pre">*args</span></code> and <code class="docutils literal notranslate"><span class="pre">**kwargs</span></code> will be passed to <code class="docutils literal notranslate"><span class="pre">callback</span></code> every <code class="docutils literal notranslate"><span class="pre">interval</span></code> seconds. If <code class="docutils literal notranslate"><span class="pre">persistent</span></code>
|
||
is <code class="docutils literal notranslate"><span class="pre">False</span></code>, this subscription will be wiped by a <em>server shutdown</em> (it will still survive a normal reload).</p>
|
||
<p>Tickers are identified and stored by making a key of the callable itself, the ticker-interval, the <code class="docutils literal notranslate"><span class="pre">persistent</span></code> flag and the <code class="docutils literal notranslate"><span class="pre">idstring</span></code> (the latter being an empty string when not given explicitly).</p>
|
||
<p>Since the arguments are not included in the ticker’s identification, the <code class="docutils literal notranslate"><span class="pre">idstring</span></code> must be used to have a specific callback triggered multiple times on the same interval but with different arguments:</p>
|
||
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span> <span class="n">tickerhandler</span><span class="o">.</span><span class="n">add</span><span class="p">(</span><span class="mi">10</span><span class="p">,</span> <span class="n">obj</span><span class="o">.</span><span class="n">update</span><span class="p">,</span> <span class="s2">"ticker1"</span><span class="p">,</span> <span class="kc">True</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="mi">3</span><span class="p">)</span>
|
||
<span class="n">tickerhandler</span><span class="o">.</span><span class="n">add</span><span class="p">(</span><span class="mi">10</span><span class="p">,</span> <span class="n">obj</span><span class="o">.</span><span class="n">update</span><span class="p">,</span> <span class="s2">"ticker2"</span><span class="p">,</span> <span class="kc">True</span><span class="p">,</span> <span class="mi">4</span><span class="p">,</span> <span class="mi">5</span><span class="p">)</span>
|
||
</pre></div>
|
||
</div>
|
||
<blockquote>
|
||
<div><p>Note that, when we want to send arguments to our callback within a ticker handler, we need to specify <code class="docutils literal notranslate"><span class="pre">idstring</span></code> and <code class="docutils literal notranslate"><span class="pre">persistent</span></code> before, unless we call our arguments as keywords, which would often be more readable:</p>
|
||
</div></blockquote>
|
||
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span> <span class="n">tickerhandler</span><span class="o">.</span><span class="n">add</span><span class="p">(</span><span class="mi">10</span><span class="p">,</span> <span class="n">obj</span><span class="o">.</span><span class="n">update</span><span class="p">,</span> <span class="n">caller</span><span class="o">=</span><span class="bp">self</span><span class="p">,</span> <span class="n">value</span><span class="o">=</span><span class="mi">118</span><span class="p">)</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>If you add a ticker with exactly the same combination of callback, interval and idstring, it will
|
||
overload the existing ticker. This identification is also crucial for later removing (stopping) the subscription:</p>
|
||
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span> <span class="n">tickerhandler</span><span class="o">.</span><span class="n">remove</span><span class="p">(</span><span class="mi">10</span><span class="p">,</span> <span class="n">obj</span><span class="o">.</span><span class="n">update</span><span class="p">,</span> <span class="n">idstring</span><span class="o">=</span><span class="s2">"ticker1"</span><span class="p">)</span>
|
||
<span class="n">tickerhandler</span><span class="o">.</span><span class="n">remove</span><span class="p">(</span><span class="mi">10</span><span class="p">,</span> <span class="n">obj</span><span class="o">.</span><span class="n">update</span><span class="p">,</span> <span class="n">idstring</span><span class="o">=</span><span class="s2">"ticker2"</span><span class="p">)</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>The <code class="docutils literal notranslate"><span class="pre">callable</span></code> can be on any form as long as it accepts the arguments you give to send to it in <code class="docutils literal notranslate"><span class="pre">TickerHandler.add</span></code>.</p>
|
||
<p>When testing, you can stop all tickers in the entire game with <code class="docutils literal notranslate"><span class="pre">tickerhandler.clear()</span></code>. You can also view the currently subscribed objects with <code class="docutils literal notranslate"><span class="pre">tickerhandler.all()</span></code>.</p>
|
||
<p>See the <a class="reference internal" href="../Howtos/Tutorial-Weather-Effects.html"><span class="doc std std-doc">Weather Tutorial</span></a> for an example of using the TickerHandler.</p>
|
||
<section id="when-not-to-use-tickerhandler">
|
||
<h3>When <em>not</em> to use TickerHandler<a class="headerlink" href="#when-not-to-use-tickerhandler" title="Permalink to this headline">¶</a></h3>
|
||
<p>Using the TickerHandler may sound very useful but it is important to consider when not to use it. Even if you are used to habitually relying on tickers for everything in other code bases, stop and think about what you really need it for. This is the main point:</p>
|
||
<blockquote>
|
||
<div><p>You should <em>never</em> use a ticker to catch <em>changes</em>.</p>
|
||
</div></blockquote>
|
||
<p>Think about it - you might have to run the ticker every second to react to the change fast enough. Most likely nothing will have changed at a given moment. So you are doing pointless calls (since skipping the call gives the same result as doing it). Making sure nothing’s changed might even be computationally expensive depending on the complexity of your system. Not to mention that you might need to run the check <em>on every object in the database</em>. Every second. Just to maintain status quo …</p>
|
||
<p>Rather than checking over and over on the off-chance that something changed, consider a more proactive approach. Could you implement your rarely changing system to <em>itself</em> report when its status changes? It’s almost always much cheaper/efficient if you can do things “on demand”. Evennia itself uses hook methods for this very reason.</p>
|
||
<p>So, if you consider a ticker that will fire very often but which you expect to have no effect 99% of the time, consider handling things things some other way. A self-reporting on-demand solution is usually cheaper also for fast-updating properties. Also remember that some things may not need to be updated until someone actually is examining or using them - any interim changes happening up to that moment are pointless waste of computing time.</p>
|
||
<p>The main reason for needing a ticker is when you want things to happen to multiple objects at the same time without input from something else.</p>
|
||
</section>
|
||
</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="Signals.html" title="Signals"
|
||
>next</a> |</li>
|
||
<li class="right" >
|
||
<a href="MonitorHandler.html" title="MonitorHandler"
|
||
>previous</a> |</li>
|
||
<li class="nav-item nav-item-0"><a href="../index.html">Evennia 3.x</a> »</li>
|
||
<li class="nav-item nav-item-1"><a href="Components-Overview.html" >Core Components</a> »</li>
|
||
<li class="nav-item nav-item-this"><a href="">TickerHandler</a></li>
|
||
</ul>
|
||
</div>
|
||
|
||
|
||
<div class="admonition important">
|
||
<p class="first admonition-title">Note</p>
|
||
<p class="last">You are reading an old version of the Evennia documentation. <a href="https://www.evennia.com/docs/latest/index.html">The latest version is here</a></p>.
|
||
</div>
|
||
|
||
|
||
<div class="footer" role="contentinfo">
|
||
© Copyright 2023, The Evennia developer community.
|
||
Created using <a href="https://www.sphinx-doc.org/">Sphinx</a> 3.2.1.
|
||
</div>
|
||
</body>
|
||
</html> |