evennia/docs/3.x/Howtos/Tutorial-Persistent-Handler.html

359 lines
30 KiB
HTML
Raw Normal View History

2023-12-20 23:10:55 +01:00
<!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>Making a Persistent object Handler &#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="Changing game calendar and time speed" href="Howto-Game-Time.html" />
<link rel="prev" title="Building a train that moves" href="Tutorial-Building-a-Train.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-Game-Time.html" title="Changing game calendar and time speed"
accesskey="N">next</a> |</li>
<li class="right" >
<a href="Tutorial-Building-a-Train.html" title="Building a train that moves"
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="">Making a Persistent object Handler</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="#">Making a Persistent object Handler</a><ul>
<li><a class="reference internal" href="#base-handler-example">Base Handler example</a></li>
<li><a class="reference internal" href="#persistent-storage-of-data-in-handler">Persistent storage of data in handler</a><ul>
<li><a class="reference internal" href="#handler-with-save-load-capability">Handler with save/load capability</a></li>
<li><a class="reference internal" href="#make-quests-storable">Make quests storable</a></li>
<li><a class="reference internal" href="#tying-it-all-together">Tying it all together</a></li>
</ul>
</li>
</ul>
</li>
</ul>
<h4>Previous topic</h4>
<p class="topless"><a href="Tutorial-Building-a-Train.html"
title="previous chapter">Building a train that moves</a></p>
<h4>Next topic</h4>
<p class="topless"><a href="Howto-Game-Time.html"
title="next chapter">Changing game calendar and time speed</a></p>
<div role="note" aria-label="source link">
<!--h3>This Page</h3-->
<ul class="this-page-menu">
<li><a href="../_sources/Howtos/Tutorial-Persistent-Handler.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="making-a-persistent-object-handler">
<h1>Making a Persistent object Handler<a class="headerlink" href="#making-a-persistent-object-handler" title="Permalink to this headline"></a></h1>
<p>A <em>handler</em> is a convenient way to group functionality on an object. This allows you to logically
group all actions related to that thing in one place. This tutorial expemplifies how to make your
own handlers and make sure data you store in them survives a reload.</p>
<p>For example, when you do <code class="docutils literal notranslate"><span class="pre">obj.attributes.get(&quot;key&quot;)</span></code> or <code class="docutils literal notranslate"><span class="pre">obj.tags.add('tagname')</span></code> you are evoking
handlers stored as <code class="docutils literal notranslate"><span class="pre">.attributes</span></code> and <code class="docutils literal notranslate"><span class="pre">tags</span></code> on the <code class="docutils literal notranslate"><span class="pre">obj</span></code>. On these handlers are methods (<code class="docutils literal notranslate"><span class="pre">get()</span></code>
and <code class="docutils literal notranslate"><span class="pre">add()</span></code> in this example).</p>
<section id="base-handler-example">
<h2>Base Handler example<a class="headerlink" href="#base-handler-example" title="Permalink to this headline"></a></h2>
<p>Here is a base way to set up an on-object handler:</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">DefaultObject</span><span class="p">,</span> <span class="n">create_object</span>
<span class="kn">from</span> <span class="nn">evennia.utils.utils</span> <span class="kn">import</span> <span class="n">lazy_property</span>
<span class="k">class</span> <span class="nc">NameChanger</span><span class="p">:</span>
<span class="k">def</span> <span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">obj</span><span class="p">):</span>
<span class="bp">self</span><span class="o">.</span><span class="n">obj</span> <span class="o">=</span> <span class="n">obj</span>
<span class="k">def</span> <span class="nf">add_to_key</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">suffix</span><span class="p">):</span>
<span class="bp">self</span><span class="o">.</span><span class="n">obj</span><span class="o">.</span><span class="n">key</span> <span class="o">=</span> <span class="sa">f</span><span class="s2">&quot;self.obj.key_</span><span class="si">{</span><span class="n">suffix</span><span class="si">}</span><span class="s2">&quot;</span>
<span class="c1"># make a test object</span>
<span class="k">class</span> <span class="nc">MyObject</span><span class="p">(</span><span class="n">DefaultObject</span><span class="p">):</span>
<span class="nd">@lazy_property</span><span class="p">:</span>
<span class="k">def</span> <span class="nf">namechange</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="k">return</span> <span class="n">NameChanger</span><span class="p">(</span><span class="bp">self</span><span class="p">)</span>
<span class="n">obj</span> <span class="o">=</span> <span class="n">create_object</span><span class="p">(</span><span class="n">MyObject</span><span class="p">,</span> <span class="n">key</span><span class="o">=</span><span class="s2">&quot;test&quot;</span><span class="p">)</span>
<span class="nb">print</span><span class="p">(</span><span class="n">obj</span><span class="o">.</span><span class="n">key</span><span class="p">)</span>
<span class="o">&gt;&gt;&gt;</span> <span class="s2">&quot;test&quot;</span>
<span class="n">obj</span><span class="o">.</span><span class="n">namechange</span><span class="o">.</span><span class="n">add_to_key</span><span class="p">(</span><span class="s2">&quot;extra&quot;</span><span class="p">)</span>
<span class="nb">print</span><span class="p">(</span><span class="n">obj</span><span class="o">.</span><span class="n">key</span><span class="p">)</span>
<span class="o">&gt;&gt;&gt;</span> <span class="s2">&quot;test_extra&quot;</span>
</pre></div>
</div>
<p>What happens here is that we make a new class <code class="docutils literal notranslate"><span class="pre">NameChanger</span></code>. We use the
<code class="docutils literal notranslate"><span class="pre">&#64;lazy_property</span></code> decorator to set it up - this means the handler will not be
actually created until someone really wants to use it, by accessing
<code class="docutils literal notranslate"><span class="pre">obj.namechange</span></code> later. The decorated <code class="docutils literal notranslate"><span class="pre">namechange</span></code> method returns the handler
and makes sure to initialize it with <code class="docutils literal notranslate"><span class="pre">self</span></code> - this becomes the <code class="docutils literal notranslate"><span class="pre">obj</span></code> inside the
handler!</p>
<p>We then make a silly method <code class="docutils literal notranslate"><span class="pre">add_to_key</span></code> that uses the handler to manipulate the
key of the object. In this example, the handler is pretty pointless, but
grouping functionality this way can both make for an easy-to-remember API and
can also allow you cache data for easy access - this is how the
<code class="docutils literal notranslate"><span class="pre">AttributeHandler</span></code> (<code class="docutils literal notranslate"><span class="pre">.attributes</span></code>) and <code class="docutils literal notranslate"><span class="pre">TagHandler</span></code> (<code class="docutils literal notranslate"><span class="pre">.tags</span></code>) works.</p>
</section>
<section id="persistent-storage-of-data-in-handler">
<h2>Persistent storage of data in handler<a class="headerlink" href="#persistent-storage-of-data-in-handler" title="Permalink to this headline"></a></h2>
<p>Lets say we want to track quests in our handler. A quest is a regular class
that represents the quest. Lets make it simple as an example:</p>
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="c1"># for example in mygame/world/quests.py</span>
<span class="k">class</span> <span class="nc">Quest</span><span class="p">:</span>
<span class="n">key</span> <span class="o">=</span> <span class="s2">&quot;The quest for the red key&quot;</span>
<span class="k">def</span> <span class="fm">__init__</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">current_step</span> <span class="o">=</span> <span class="s2">&quot;start&quot;</span>
<span class="k">def</span> <span class="nf">check_progress</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="c1"># uses self.current_step to check</span>
<span class="c1"># progress of this quest</span>
<span class="nb">getattr</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="sa">f</span><span class="s2">&quot;step_</span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">current_step</span><span class="si">}</span><span class="s2">&quot;</span><span class="p">)()</span>
<span class="k">def</span> <span class="nf">step_start</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="c1"># check here if quest-step is complete</span>
<span class="bp">self</span><span class="o">.</span><span class="n">current_step</span> <span class="o">=</span> <span class="s2">&quot;find_the_red_key&quot;</span>
<span class="k">def</span> <span class="nf">step_find_the_red_key</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="c1"># check if step is complete</span>
<span class="bp">self</span><span class="o">.</span><span class="n">current_step</span> <span class="o">=</span> <span class="s2">&quot;hand_in_quest&quot;</span>
<span class="k">def</span> <span class="nf">step_hand_in_quest</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="c1"># check if handed in quest to quest giver</span>
<span class="bp">self</span><span class="o">.</span><span class="n">current_step</span> <span class="o">=</span> <span class="kc">None</span> <span class="c1"># finished</span>
</pre></div>
</div>
<p>We expect the dev to make subclasses of this to implement different quests. Exactly how this works
doesnt matter, the key is that we want to track <code class="docutils literal notranslate"><span class="pre">self.current_step</span></code> - a property that <em>should
survive a server reload</em>. But so far there is no way for <code class="docutils literal notranslate"><span class="pre">Quest</span></code> to accomplish this, its just a
normal Python class with no connection to the database.</p>
<section id="handler-with-save-load-capability">
<h3>Handler with save/load capability<a class="headerlink" href="#handler-with-save-load-capability" title="Permalink to this headline"></a></h3>
<p>Lets make a <code class="docutils literal notranslate"><span class="pre">QuestHandler</span></code> that manages a characters quests.</p>
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="c1"># for example in the same mygame/world/quests.py</span>
<span class="k">class</span> <span class="nc">QuestHandler</span><span class="p">:</span>
<span class="k">def</span> <span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">obj</span><span class="p">):</span>
<span class="bp">self</span><span class="o">.</span><span class="n">obj</span> <span class="o">=</span> <span class="n">obj</span>
<span class="bp">self</span><span class="o">.</span><span class="n">do_save</span> <span class="o">=</span> <span class="kc">False</span>
<span class="bp">self</span><span class="o">.</span><span class="n">_load</span><span class="p">()</span>
<span class="k">def</span> <span class="nf">_load</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">storage</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">obj</span><span class="o">.</span><span class="n">attributes</span><span class="o">.</span><span class="n">get</span><span class="p">(</span>
<span class="s2">&quot;quest_storage&quot;</span><span class="p">,</span> <span class="n">default</span><span class="o">=</span><span class="p">{},</span> <span class="n">category</span><span class="o">=</span><span class="s2">&quot;quests&quot;</span><span class="p">)</span>
<span class="k">def</span> <span class="nf">_save</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">obj</span><span class="o">.</span><span class="n">attributes</span><span class="o">.</span><span class="n">add</span><span class="p">(</span>
<span class="s2">&quot;quest_storage&quot;</span><span class="p">,</span> <span class="bp">self</span><span class="o">.</span><span class="n">storage</span><span class="p">,</span> <span class="n">category</span><span class="o">=</span><span class="s2">&quot;quests&quot;</span><span class="p">)</span>
<span class="bp">self</span><span class="o">.</span><span class="n">_load</span><span class="p">()</span> <span class="c1"># important</span>
<span class="bp">self</span><span class="o">.</span><span class="n">do_save</span> <span class="o">=</span> <span class="kc">False</span>
<span class="k">def</span> <span class="nf">add</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">questclass</span><span class="p">):</span>
<span class="bp">self</span><span class="o">.</span><span class="n">storage</span><span class="p">[</span><span class="n">questclass</span><span class="o">.</span><span class="n">key</span><span class="p">]</span> <span class="o">=</span> <span class="n">questclass</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">obj</span><span class="p">)</span>
<span class="bp">self</span><span class="o">.</span><span class="n">_save</span><span class="p">()</span>
<span class="k">def</span> <span class="nf">check_progress</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="n">quest</span><span class="o">.</span><span class="n">check_progress</span><span class="p">()</span>
<span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">do_save</span><span class="p">:</span>
<span class="c1"># .do_save is set on handler by Quest if it wants to save progress</span>
<span class="bp">self</span><span class="o">.</span><span class="n">_save</span><span class="p">()</span>
</pre></div>
</div>
<p>The handler is just a normal Python class and has no database-storage on its own. But it has a link
to <code class="docutils literal notranslate"><span class="pre">.obj</span></code>, which is assumed to be a full typeclased entity, on which we can create
persistent <a class="reference internal" href="../Components/Attributes.html"><span class="doc std std-doc">Attributes</span></a> to store things however we like!</p>
<p>We make two helper methods <code class="docutils literal notranslate"><span class="pre">_load</span></code> and
<code class="docutils literal notranslate"><span class="pre">_save</span></code> that handles local fetches and saves <code class="docutils literal notranslate"><span class="pre">storage</span></code> to an Attribute on the object. To avoid
saving more than necessary, we have a property <code class="docutils literal notranslate"><span class="pre">do_save</span></code>. This we will set in <code class="docutils literal notranslate"><span class="pre">Quest</span></code> below.</p>
<blockquote>
<div><p>Note that once we <code class="docutils literal notranslate"><span class="pre">_save</span></code> the data, we need to call <code class="docutils literal notranslate"><span class="pre">_load</span></code> again. This is to make sure the version we store on the handler is properly de-serialized. If you get an error about data being <code class="docutils literal notranslate"><span class="pre">bytes</span></code>, you probably missed this step.</p>
</div></blockquote>
</section>
<section id="make-quests-storable">
<h3>Make quests storable<a class="headerlink" href="#make-quests-storable" title="Permalink to this headline"></a></h3>
<p>The handler will save all <code class="docutils literal notranslate"><span class="pre">Quest</span></code> objects as a <code class="docutils literal notranslate"><span class="pre">dict</span></code> in an Attribute on <code class="docutils literal notranslate"><span class="pre">obj</span></code>. We are not done yet
though, the <code class="docutils literal notranslate"><span class="pre">Quest</span></code> object needs access to the <code class="docutils literal notranslate"><span class="pre">obj</span></code> too - not only will this is important to figure
out if the quest is complete (the <code class="docutils literal notranslate"><span class="pre">Quest</span></code> must be able to check the questers inventory to see if
they have the red key, for example), it also allows the <code class="docutils literal notranslate"><span class="pre">Quest</span></code> to tell the handler when its state
changed and it should be saved.</p>
<p>We change the <code class="docutils literal notranslate"><span class="pre">Quest</span></code> such:</p>
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="kn">from</span> <span class="nn">evennia.utils</span> <span class="kn">import</span> <span class="n">dbserialize</span>
<span class="k">class</span> <span class="nc">Quest</span><span class="p">:</span>
<span class="k">def</span> <span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">obj</span><span class="p">):</span>
<span class="bp">self</span><span class="o">.</span><span class="n">obj</span> <span class="o">=</span> <span class="n">obj</span>
<span class="bp">self</span><span class="o">.</span><span class="n">_current_step</span> <span class="o">=</span> <span class="s2">&quot;start&quot;</span>
<span class="k">def</span> <span class="nf">__serialize_dbobjs__</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">obj</span> <span class="o">=</span> <span class="n">dbserialize</span><span class="o">.</span><span class="n">dbserialize</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">obj</span><span class="p">)</span>
<span class="k">def</span> <span class="nf">__deserialize_dbobjs__</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="k">if</span> <span class="nb">isinstance</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">obj</span><span class="p">,</span> <span class="nb">bytes</span><span class="p">):</span>
<span class="bp">self</span><span class="o">.</span><span class="n">obj</span> <span class="o">=</span> <span class="n">dbserialize</span><span class="o">.</span><span class="n">dbunserialize</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">obj</span><span class="p">)</span>
<span class="nd">@property</span>
<span class="k">def</span> <span class="nf">questhandler</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">obj</span><span class="o">.</span><span class="n">quests</span>
<span class="nd">@property</span>
<span class="k">def</span> <span class="nf">current_step</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">_current_step</span>
<span class="nd">@current_step</span><span class="o">.</span><span class="n">setter</span>
<span class="k">def</span> <span class="nf">current_step</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">value</span><span class="p">):</span>
<span class="bp">self</span><span class="o">.</span><span class="n">_current_step</span> <span class="o">=</span> <span class="n">value</span>
<span class="bp">self</span><span class="o">.</span><span class="n">questhandler</span><span class="o">.</span><span class="n">do_save</span> <span class="o">=</span> <span class="kc">True</span> <span class="c1"># this triggers save in handler!</span>
<span class="c1"># [same as before]</span>
</pre></div>
</div>
<p>The <code class="docutils literal notranslate"><span class="pre">Quest.__init__</span></code> now takes <code class="docutils literal notranslate"><span class="pre">obj</span></code> as argument, to match what we pass to it in
<code class="docutils literal notranslate"><span class="pre">QuestHandler.add</span></code>. We want to monitor the changing of <code class="docutils literal notranslate"><span class="pre">current_step</span></code>, so we
make it into a <code class="docutils literal notranslate"><span class="pre">property</span></code>. When we edit that value, we set the <code class="docutils literal notranslate"><span class="pre">do_save</span></code> flag on
the handler, which means it will save the status to database once it has checked
progress on all its quests. The <code class="docutils literal notranslate"><span class="pre">Quest.questhandler</span></code> property allows to easily
get back to the handler (and the object on which it sits).</p>
<p>The <code class="docutils literal notranslate"><span class="pre">__serialize__dbobjs__</span></code> and <code class="docutils literal notranslate"><span class="pre">__deserialize_dbobjs__</span></code> methods are needed
because <code class="docutils literal notranslate"><span class="pre">Attributes</span></code> cant store hidden database objects (the <code class="docutils literal notranslate"><span class="pre">Quest.obj</span></code>
property. The methods help Evennia serialize/deserialize <code class="docutils literal notranslate"><span class="pre">Quest</span></code> propertly when
the handler saves it. For more information, see <a class="reference internal" href="../Components/Attributes.html#storing-single-objects"><span class="std std-doc">Storing Single
objects</span></a> in the Attributes</p>
</section>
<section id="tying-it-all-together">
<h3>Tying it all together<a class="headerlink" href="#tying-it-all-together" title="Permalink to this headline"></a></h3>
<p>The final thing we need to do is to add the quest-handler to the character:</p>
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="c1"># in mygame/typeclasses/characters.py</span>
<span class="kn">from</span> <span class="nn">evennia</span> <span class="kn">import</span> <span class="n">DefaultCharacter</span>
<span class="kn">from</span> <span class="nn">evennia.utils.utils</span> <span class="kn">import</span> <span class="n">lazy_property</span>
<span class="kn">from</span> <span class="nn">.world.quests</span> <span class="kn">import</span> <span class="n">QuestHandler</span> <span class="c1"># as an example</span>
<span class="k">class</span> <span class="nc">Character</span><span class="p">(</span><span class="n">DefaultCharacter</span><span class="p">):</span>
<span class="c1"># ...</span>
<span class="nd">@lazy_property</span>
<span class="k">def</span> <span class="nf">quests</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="k">return</span> <span class="n">QuestHandler</span><span class="p">(</span><span class="bp">self</span><span class="p">)</span>
</pre></div>
</div>
<p>You can now make your Quest classes to describe your quests and add them to
characters with</p>
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="n">character</span><span class="o">.</span><span class="n">quests</span><span class="o">.</span><span class="n">add</span><span class="p">(</span><span class="n">FindTheRedKey</span><span class="p">)</span>
</pre></div>
</div>
<p>and can later do</p>
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="n">character</span><span class="o">.</span><span class="n">quests</span><span class="o">.</span><span class="n">check_progress</span><span class="p">()</span>
</pre></div>
</div>
<p>and be sure that quest data is not lost between reloads.</p>
<p>You can find a full-fledged quest-handler example as <a class="reference internal" href="../api/evennia.contrib.tutorials.evadventure.quests.html#evennia-contrib-tutorials-evadventure-quests"><span class="std std-ref">EvAdventure
quests</span></a> contrib in the Evennia
repository.</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="Howto-Game-Time.html" title="Changing game calendar and time speed"
>next</a> |</li>
<li class="right" >
<a href="Tutorial-Building-a-Train.html" title="Building a train that moves"
>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="">Making a Persistent object Handler</a></li>
</ul>
</div>
<div class="footer" role="contentinfo">
&#169; Copyright 2023, The Evennia developer community.
Created using <a href="https://www.sphinx-doc.org/">Sphinx</a> 3.2.1.
</div>
</body>
</html>