evennia/docs/1.0-dev/Components/Attributes.html
2021-06-13 22:50:03 +02:00

553 lines
No EOL
53 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>Attributes &#8212; Evennia 1.0-dev 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" />
</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="nav-item nav-item-0"><a href="../index.html">Evennia 1.0-dev</a> &#187;</li>
<li class="nav-item nav-item-this"><a href="">Attributes</a></li>
</ul>
</div>
<div class="document">
<div class="documentwrapper">
<div class="bodywrapper">
<div class="body" role="main">
<section id="attributes">
<h1>Attributes<a class="headerlink" href="#attributes" title="Permalink to this headline"></a></h1>
<p>When performing actions in Evennia it is often important that you store data for later. If you write
a menu system, you have to keep track of the current location in the menu tree so that the player
can give correct subsequent commands. If you are writing a combat system, you might have a
combattants next roll get easier dependent on if their opponent failed. Your characters will
probably need to store roleplaying-attributes like strength and agility. And so on.</p>
<p><a class="reference internal" href="Typeclasses.html"><span class="doc">Typeclassed</span></a> game entities (<a class="reference internal" href="Accounts.html"><span class="doc">Accounts</span></a>, <a class="reference internal" href="Objects.html"><span class="doc">Objects</span></a>,
<a class="reference internal" href="Scripts.html"><span class="doc">Scripts</span></a> and <a class="reference internal" href="Communications.html"><span class="doc">Channels</span></a>) always have <em>Attributes</em> associated with them.
Attributes are used to store any type of data on such entities. This is different from storing
data in properties already defined on entities (such as <code class="docutils literal notranslate"><span class="pre">key</span></code> or <code class="docutils literal notranslate"><span class="pre">location</span></code>) - these have very
specific names and require very specific types of data (for example you couldnt assign a python
<em>list</em> to the <code class="docutils literal notranslate"><span class="pre">key</span></code> property no matter how hard you tried). <code class="docutils literal notranslate"><span class="pre">Attributes</span></code> come into play when you
want to assign arbitrary data to arbitrary names.</p>
<p><strong>Attributes are <em>not</em> secure by default and any player may be able to change them unless you
<a class="reference external" href="Components/Attributes.html#locking-and-checking-attributes">prevent this behavior</a>.</strong></p>
<section id="the-db-and-ndb-shortcuts">
<h2>The .db and .ndb shortcuts<a class="headerlink" href="#the-db-and-ndb-shortcuts" title="Permalink to this headline"></a></h2>
<p>To save persistent data on a Typeclassed object you normally use the <code class="docutils literal notranslate"><span class="pre">db</span></code> (DataBase) operator. Lets
try to save some data to a <em>Rose</em> (an <a class="reference internal" href="Objects.html"><span class="doc">Object</span></a>):</p>
<div class="highlight-python notranslate"><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></pre></div></td><td class="code"><div class="highlight"><pre><span></span> <span class="c1"># saving</span>
<span class="n">rose</span><span class="o">.</span><span class="n">db</span><span class="o">.</span><span class="n">has_thorns</span> <span class="o">=</span> <span class="kc">True</span>
<span class="c1"># getting it back</span>
<span class="n">is_ouch</span> <span class="o">=</span> <span class="n">rose</span><span class="o">.</span><span class="n">db</span><span class="o">.</span><span class="n">has_thorns</span>
</pre></div>
</td></tr></table></div>
<p>This looks like any normal Python assignment, but that <code class="docutils literal notranslate"><span class="pre">db</span></code> makes sure that an <em>Attribute</em> is
created behind the scenes and is stored in the database. Your rose will continue to have thorns
throughout the life of the server now, until you deliberately remove them.</p>
<p>To be sure to save <strong>non-persistently</strong>, i.e. to make sure NOT to create a database entry, you use
<code class="docutils literal notranslate"><span class="pre">ndb</span></code> (NonDataBase). It works in the same way:</p>
<div class="highlight-python notranslate"><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></pre></div></td><td class="code"><div class="highlight"><pre><span></span> <span class="c1"># saving</span>
<span class="n">rose</span><span class="o">.</span><span class="n">ndb</span><span class="o">.</span><span class="n">has_thorns</span> <span class="o">=</span> <span class="kc">True</span>
<span class="c1"># getting it back</span>
<span class="n">is_ouch</span> <span class="o">=</span> <span class="n">rose</span><span class="o">.</span><span class="n">ndb</span><span class="o">.</span><span class="n">has_thorns</span>
</pre></div>
</td></tr></table></div>
<p>Technically, <code class="docutils literal notranslate"><span class="pre">ndb</span></code> has nothing to do with <code class="docutils literal notranslate"><span class="pre">Attributes</span></code>, despite how similar they look. No
<code class="docutils literal notranslate"><span class="pre">Attribute</span></code> object is created behind the scenes when using <code class="docutils literal notranslate"><span class="pre">ndb</span></code>. In fact the database is not
invoked at all since we are not interested in persistence. There is however an important reason to
use <code class="docutils literal notranslate"><span class="pre">ndb</span></code> to store data rather than to just store variables direct on entities - <code class="docutils literal notranslate"><span class="pre">ndb</span></code>-stored data
is tracked by the server and will not be purged in various cache-cleanup operations Evennia may do
while it runs. Data stored on <code class="docutils literal notranslate"><span class="pre">ndb</span></code> (as well as <code class="docutils literal notranslate"><span class="pre">db</span></code>) will also be easily listed by example the
<code class="docutils literal notranslate"><span class="pre">&#64;examine</span></code> command.</p>
<p>You can also <code class="docutils literal notranslate"><span class="pre">del</span></code> properties on <code class="docutils literal notranslate"><span class="pre">db</span></code> and <code class="docutils literal notranslate"><span class="pre">ndb</span></code> as normal. This will for example delete an
<code class="docutils literal notranslate"><span class="pre">Attribute</span></code>:</p>
<div class="highlight-python notranslate"><table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre><span class="normal">1</span></pre></div></td><td class="code"><div class="highlight"><pre><span></span> <span class="k">del</span> <span class="n">rose</span><span class="o">.</span><span class="n">db</span><span class="o">.</span><span class="n">has_thorns</span>
</pre></div>
</td></tr></table></div>
<p>Both <code class="docutils literal notranslate"><span class="pre">db</span></code> and <code class="docutils literal notranslate"><span class="pre">ndb</span></code> defaults to offering an <code class="docutils literal notranslate"><span class="pre">all</span></code> property on themselves. This returns all
associated attributes or non-persistent properties.</p>
<div class="highlight-python notranslate"><table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre><span class="normal">1</span>
<span class="normal">2</span></pre></div></td><td class="code"><div class="highlight"><pre><span></span> <span class="n">list_of_all_rose_attributes</span> <span class="o">=</span> <span class="n">rose</span><span class="o">.</span><span class="n">db</span><span class="o">.</span><span class="n">all</span>
<span class="n">list_of_all_rose_ndb_attrs</span> <span class="o">=</span> <span class="n">rose</span><span class="o">.</span><span class="n">ndb</span><span class="o">.</span><span class="n">all</span>
</pre></div>
</td></tr></table></div>
<p>If you use <code class="docutils literal notranslate"><span class="pre">all</span></code> as the name of an attribute, this will be used instead. Later deleting your custom
<code class="docutils literal notranslate"><span class="pre">all</span></code> will return the default behaviour.</p>
</section>
<section id="the-attributehandler">
<h2>The AttributeHandler<a class="headerlink" href="#the-attributehandler" title="Permalink to this headline"></a></h2>
<p>The <code class="docutils literal notranslate"><span class="pre">.db</span></code> and <code class="docutils literal notranslate"><span class="pre">.ndb</span></code> properties are very convenient but if you dont know the name of the Attribute
beforehand they cannot be used. Behind the scenes <code class="docutils literal notranslate"><span class="pre">.db</span></code> actually accesses the <code class="docutils literal notranslate"><span class="pre">AttributeHandler</span></code>
which sits on typeclassed entities as the <code class="docutils literal notranslate"><span class="pre">.attributes</span></code> property. <code class="docutils literal notranslate"><span class="pre">.ndb</span></code> does the same for the
<code class="docutils literal notranslate"><span class="pre">.nattributes</span></code> property.</p>
<p>The handlers have normal access methods that allow you to manage and retrieve <code class="docutils literal notranslate"><span class="pre">Attributes</span></code> and
<code class="docutils literal notranslate"><span class="pre">NAttributes</span></code>:</p>
<ul class="simple">
<li><p><code class="docutils literal notranslate"><span class="pre">has('attrname')</span></code> - this checks if the object has an Attribute with this key. This is equivalent
to doing <code class="docutils literal notranslate"><span class="pre">obj.db.attrname</span></code>.</p></li>
<li><p><code class="docutils literal notranslate"><span class="pre">get(...)</span></code> - this retrieves the given Attribute. Normally the <code class="docutils literal notranslate"><span class="pre">value</span></code> property of the Attribute is
returned, but the method takes keywords for returning the Attribute object itself. By supplying an
<code class="docutils literal notranslate"><span class="pre">accessing_object</span></code> to the call one can also make sure to check permissions before modifying
anything.</p></li>
<li><p><code class="docutils literal notranslate"><span class="pre">add(...)</span></code> - this adds a new Attribute to the object. An optional <a class="reference internal" href="Locks.html"><span class="doc">lockstring</span></a> can be
supplied here to restrict future access and also the call itself may be checked against locks.</p></li>
<li><p><code class="docutils literal notranslate"><span class="pre">remove(...)</span></code> - Remove the given Attribute. This can optionally be made to check for permission
before performing the deletion. - <code class="docutils literal notranslate"><span class="pre">clear(...)</span></code> - removes all Attributes from object.</p></li>
<li><p><code class="docutils literal notranslate"><span class="pre">all(...)</span></code> - returns all Attributes (of the given category) attached to this object.</p></li>
</ul>
<p>See <a class="reference external" href="Components/Attributes.html#locking-and-checking-attributes">this section</a> for more about locking down Attribute
access and editing. The <code class="docutils literal notranslate"><span class="pre">Nattribute</span></code> offers no concept of access control.</p>
<p>Some examples:</p>
<div class="highlight-python notranslate"><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></pre></div></td><td class="code"><div class="highlight"><pre><span></span> <span class="kn">import</span> <span class="nn">evennia</span>
<span class="n">obj</span> <span class="o">=</span> <span class="n">evennia</span><span class="o">.</span><span class="n">search_object</span><span class="p">(</span><span class="s2">&quot;MyObject&quot;</span><span class="p">)</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;test&quot;</span><span class="p">,</span> <span class="s2">&quot;testvalue&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">db</span><span class="o">.</span><span class="n">test</span><span class="p">)</span> <span class="c1"># prints &quot;testvalue&quot;</span>
<span class="nb">print</span><span class="p">(</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;test&quot;</span><span class="p">))</span> <span class="c1"># &quot;</span>
<span class="nb">print</span><span class="p">(</span><span class="n">obj</span><span class="o">.</span><span class="n">attributes</span><span class="o">.</span><span class="n">all</span><span class="p">())</span> <span class="c1"># prints [&lt;AttributeObject&gt;]</span>
<span class="n">obj</span><span class="o">.</span><span class="n">attributes</span><span class="o">.</span><span class="n">remove</span><span class="p">(</span><span class="s2">&quot;test&quot;</span><span class="p">)</span>
</pre></div>
</td></tr></table></div>
</section>
<section id="properties-of-attributes">
<h2>Properties of Attributes<a class="headerlink" href="#properties-of-attributes" title="Permalink to this headline"></a></h2>
<p>An Attribute object is stored in the database. It has the following properties:</p>
<ul class="simple">
<li><p><code class="docutils literal notranslate"><span class="pre">key</span></code> - the name of the Attribute. When doing e.g. <code class="docutils literal notranslate"><span class="pre">obj.db.attrname</span> <span class="pre">=</span> <span class="pre">value</span></code>, this property is set
to <code class="docutils literal notranslate"><span class="pre">attrname</span></code>.</p></li>
<li><p><code class="docutils literal notranslate"><span class="pre">value</span></code> - this is the value of the Attribute. This value can be anything which can be pickled -
objects, lists, numbers or what have you (see
<a class="reference external" href="Components/Attributes.html#What_types_of_data_can_I_save_in_an_Attribute">this section</a> for more info). In the
example
<code class="docutils literal notranslate"><span class="pre">obj.db.attrname</span> <span class="pre">=</span> <span class="pre">value</span></code>, the <code class="docutils literal notranslate"><span class="pre">value</span></code> is stored here.</p></li>
<li><p><code class="docutils literal notranslate"><span class="pre">category</span></code> - this is an optional property that is set to None for most Attributes. Setting this
allows to use Attributes for different functionality. This is usually not needed unless you want
to use Attributes for very different functionality (<a class="reference internal" href="Nicks.html"><span class="doc">Nicks</span></a> is an example of using
Attributes
in this way). To modify this property you need to use the <a class="reference external" href="Components/Attributes.html#The_Attribute_Handler">Attribute
Handler</a>.</p></li>
<li><p><code class="docutils literal notranslate"><span class="pre">strvalue</span></code> - this is a separate value field that only accepts strings. This severely limits the
data possible to store, but allows for easier database lookups. This property is usually not used
except when re-using Attributes for some other purpose (<a class="reference internal" href="Nicks.html"><span class="doc">Nicks</span></a> use it). It is only
accessible via the <a class="reference external" href="Components/Attributes.html#The_Attribute_Handler">Attribute Handler</a>.</p></li>
</ul>
<p>There are also two special properties:</p>
<ul class="simple">
<li><p><code class="docutils literal notranslate"><span class="pre">attrtype</span></code> - this is used internally by Evennia to separate <a class="reference internal" href="Nicks.html"><span class="doc">Nicks</span></a>, from Attributes (Nicks
use Attributes behind the scenes).</p></li>
<li><p><code class="docutils literal notranslate"><span class="pre">model</span></code> - this is a <em>natural-key</em> describing the model this Attribute is attached to. This is on
the form <em>appname.modelclass</em>, like <code class="docutils literal notranslate"><span class="pre">objects.objectdb</span></code>. It is used by the Attribute and
NickHandler to quickly sort matches in the database. Neither this nor <code class="docutils literal notranslate"><span class="pre">attrtype</span></code> should normally
need to be modified.</p></li>
</ul>
<p>Non-database attributes have no equivalence to <code class="docutils literal notranslate"><span class="pre">category</span></code> nor <code class="docutils literal notranslate"><span class="pre">strvalue</span></code>, <code class="docutils literal notranslate"><span class="pre">attrtype</span></code> or <code class="docutils literal notranslate"><span class="pre">model</span></code>.</p>
</section>
<section id="persistent-vs-non-persistent">
<h2>Persistent vs non-persistent<a class="headerlink" href="#persistent-vs-non-persistent" title="Permalink to this headline"></a></h2>
<p>So <em>persistent</em> data means that your data will survive a server reboot, whereas with
<em>non-persistent</em> data it will not …</p>
<p>… So why would you ever want to use non-persistent data? The answer is, you dont have to. Most of
the time you really want to save as much as you possibly can. Non-persistent data is potentially
useful in a few situations though.</p>
<ul class="simple">
<li><p>You are worried about database performance. Since Evennia caches Attributes very aggressively,
this is not an issue unless you are reading <em>and</em> writing to your Attribute very often (like many
times per second). Reading from an already cached Attribute is as fast as reading any Python
property. But even then this is not likely something to worry about: Apart from Evennias own
caching, modern database systems themselves also cache data very efficiently for speed. Our
default
database even runs completely in RAM if possible, alleviating much of the need to write to disk
during heavy loads.</p></li>
<li><p>A more valid reason for using non-persistent data is if you <em>want</em> to lose your state when logging
off. Maybe you are storing throw-away data that are re-initialized at server startup. Maybe you
are implementing some caching of your own. Or maybe you are testing a buggy <a class="reference internal" href="Scripts.html"><span class="doc">Script</span></a> that
does potentially harmful stuff to your character object. With non-persistent storage you can be
sure
that whatever is messed up, its nothing a server reboot cant clear up.</p></li>
<li><p>NAttributes have no restrictions at all on what they can store (see next section), since they
dont need to worry about being saved to the database - they work very well for temporary storage.</p></li>
<li><p>You want to implement a fully or partly <em>non-persistent world</em>. Who are we to argue with your
grand vision!</p></li>
</ul>
</section>
<section id="what-types-of-data-can-i-save-in-an-attribute">
<h2>What types of data can I save in an Attribute?<a class="headerlink" href="#what-types-of-data-can-i-save-in-an-attribute" title="Permalink to this headline"></a></h2>
<blockquote>
<div><p>None of the following affects NAttributes, which does not invoke the database at all. There are no
restrictions to what can be stored in a NAttribute.</p>
</div></blockquote>
<p>The database doesnt know anything about Python objects, so Evennia must <em>serialize</em> Attribute
values into a string representation in order to store it to the database. This is done using the
<code class="docutils literal notranslate"><span class="pre">pickle</span></code> module of Python (the only exception is if you use the <code class="docutils literal notranslate"><span class="pre">strattr</span></code> keyword of the
AttributeHandler to save to the <code class="docutils literal notranslate"><span class="pre">strvalue</span></code> field of the Attribute. In that case you can only save
<em>strings</em> which will not be pickled).</p>
<p>Its important to note that when you access the data in an Attribute you are <em>always</em> de-serializing
it from the database representation every time. This is because we allow for storing
database-entities in Attributes too. If we cached it as its Python form, we might end up with
situations where the database entity was deleted since we last accessed the Attribute.
De-serializing data with a database-entity in it means querying the database for that object and
making sure it still exists (otherwise it will be set to <code class="docutils literal notranslate"><span class="pre">None</span></code>). Performance-wise this is usually
not a big deal. But if you are accessing the Attribute as part of some big loop or doing a large
amount of reads/writes you should first extract it to a temporary variable, operate on <em>that</em> and
then save the result back to the Attribute. If you are storing a more complex structure like a
<code class="docutils literal notranslate"><span class="pre">dict</span></code> or a <code class="docutils literal notranslate"><span class="pre">list</span></code> you should make sure to “disconnect” it from the database before looping over it,
as mentioned in the <a class="reference external" href="Components/Attributes.html#retrieving-mutable-objects">Retrieving Mutable Objects</a> section
below.</p>
<section id="storing-single-objects">
<h3>Storing single objects<a class="headerlink" href="#storing-single-objects" title="Permalink to this headline"></a></h3>
<p>With a single object, we mean anything that is <em>not iterable</em>, like numbers, strings or custom class
instances without the <code class="docutils literal notranslate"><span class="pre">__iter__</span></code> method.</p>
<ul class="simple">
<li><p>You can generally store any non-iterable Python entity that can be
<a class="reference external" href="http://docs.python.org/library/pickle.html">pickled</a>.</p></li>
<li><p>Single database objects/typeclasses can be stored as any other in the Attribute. These can
normally <em>not</em> be pickled, but Evennia will behind the scenes convert them to an internal
representation using their classname, database-id and creation-date with a microsecond precision,
guaranteeing you get the same object back when you access the Attribute later.</p></li>
<li><p>If you <em>hide</em> a database object inside a non-iterable custom class (like stored as a variable
inside it), Evennia will not know its there and wont convert it safely. Storing classes with
such hidden database objects is <em>not</em> supported and will lead to errors!</p></li>
</ul>
<div class="highlight-python notranslate"><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></pre></div></td><td class="code"><div class="highlight"><pre><span></span><span class="c1"># Examples of valid single-value attribute data:</span>
<span class="n">obj</span><span class="o">.</span><span class="n">db</span><span class="o">.</span><span class="n">test1</span> <span class="o">=</span> <span class="mi">23</span>
<span class="n">obj</span><span class="o">.</span><span class="n">db</span><span class="o">.</span><span class="n">test1</span> <span class="o">=</span> <span class="kc">False</span>
<span class="c1"># a database object (will be stored as an internal representation)</span>
<span class="n">obj</span><span class="o">.</span><span class="n">db</span><span class="o">.</span><span class="n">test2</span> <span class="o">=</span> <span class="n">myobj</span>
<span class="c1"># example of an invalid, &quot;hidden&quot; dbobject</span>
<span class="k">class</span> <span class="nc">Invalid</span><span class="p">(</span><span class="nb">object</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">dbobj</span><span class="p">):</span>
<span class="c1"># no way for Evennia to know this is a dbobj</span>
<span class="bp">self</span><span class="o">.</span><span class="n">dbobj</span> <span class="o">=</span> <span class="n">dbobj</span>
<span class="n">invalid</span> <span class="o">=</span> <span class="n">Invalid</span><span class="p">(</span><span class="n">myobj</span><span class="p">)</span>
<span class="n">obj</span><span class="o">.</span><span class="n">db</span><span class="o">.</span><span class="n">invalid</span> <span class="o">=</span> <span class="n">invalid</span> <span class="c1"># will cause error!</span>
</pre></div>
</td></tr></table></div>
</section>
<section id="storing-multiple-objects">
<h3>Storing multiple objects<a class="headerlink" href="#storing-multiple-objects" title="Permalink to this headline"></a></h3>
<p>This means storing objects in a collection of some kind and are examples of <em>iterables</em>, pickle-able
entities you can loop over in a for-loop. Attribute-saving supports the following iterables:</p>
<ul class="simple">
<li><p><a class="reference external" href="https://docs.python.org/2/library/functions.html#tuple">Tuples</a>, like <code class="docutils literal notranslate"><span class="pre">(1,2,&quot;test&quot;,</span> <span class="pre">&lt;dbobj&gt;)</span></code>.</p></li>
<li><p><a class="reference external" href="https://docs.python.org/2/tutorial/datastructures.html#more-on-lists">Lists</a>, like <code class="docutils literal notranslate"><span class="pre">[1,2,&quot;test&quot;,</span> <span class="pre">&lt;dbobj&gt;]</span></code>.</p></li>
<li><p><a class="reference external" href="https://docs.python.org/2/tutorial/datastructures.html#dictionaries">Dicts</a>, like <code class="docutils literal notranslate"><span class="pre">{1:2,</span> <span class="pre">&quot;test&quot;:&lt;dbobj&gt;]</span></code>.</p></li>
<li><p><a class="reference external" href="https://docs.python.org/2/tutorial/datastructures.html#sets">Sets</a>, like <code class="docutils literal notranslate"><span class="pre">{1,2,&quot;test&quot;,&lt;dbobj&gt;}</span></code>.</p></li>
<li></li>
</ul>
<p><a class="reference external" href="https://docs.python.org/2/library/collections.html#collections.OrderedDict">collections.OrderedDict</a>,
like <code class="docutils literal notranslate"><span class="pre">OrderedDict((1,2),</span> <span class="pre">(&quot;test&quot;,</span> <span class="pre">&lt;dbobj&gt;))</span></code>.</p>
<ul class="simple">
<li><p><a class="reference external" href="https://docs.python.org/2/library/collections.html#collections.deque">collections.Deque</a>, like
<code class="docutils literal notranslate"><span class="pre">deque((1,2,&quot;test&quot;,&lt;dbobj&gt;))</span></code>.</p></li>
<li><p><em>Nestings</em> of any combinations of the above, like lists in dicts or an OrderedDict of tuples, each
containing dicts, etc.</p></li>
<li><p>All other iterables (i.e. entities with the <code class="docutils literal notranslate"><span class="pre">__iter__</span></code> method) will be converted to a <em>list</em>.
Since you can use any combination of the above iterables, this is generally not much of a
limitation.</p></li>
</ul>
<p>Any entity listed in the <a class="reference external" href="Components/Attributes.html#Storing-Single-Objects">Single object</a> section above can be
stored in the iterable.</p>
<blockquote>
<div><p>As mentioned in the previous section, database entities (aka typeclasses) are not possible to
pickle. So when storing an iterable, Evennia must recursively traverse the iterable <em>and all its
nested sub-iterables</em> in order to find eventual database objects to convert. This is a very fast
process but for efficiency you may want to avoid too deeply nested structures if you can.</p>
</div></blockquote>
<div class="highlight-python notranslate"><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></pre></div></td><td class="code"><div class="highlight"><pre><span></span><span class="c1"># examples of valid iterables to store</span>
<span class="n">obj</span><span class="o">.</span><span class="n">db</span><span class="o">.</span><span class="n">test3</span> <span class="o">=</span> <span class="p">[</span><span class="n">obj1</span><span class="p">,</span> <span class="mi">45</span><span class="p">,</span> <span class="n">obj2</span><span class="p">,</span> <span class="mi">67</span><span class="p">]</span>
<span class="c1"># a dictionary</span>
<span class="n">obj</span><span class="o">.</span><span class="n">db</span><span class="o">.</span><span class="n">test4</span> <span class="o">=</span> <span class="p">{</span><span class="s1">&#39;str&#39;</span><span class="p">:</span><span class="mi">34</span><span class="p">,</span> <span class="s1">&#39;dex&#39;</span><span class="p">:</span><span class="mi">56</span><span class="p">,</span> <span class="s1">&#39;agi&#39;</span><span class="p">:</span><span class="mi">22</span><span class="p">,</span> <span class="s1">&#39;int&#39;</span><span class="p">:</span><span class="mi">77</span><span class="p">}</span>
<span class="c1"># a mixed dictionary/list</span>
<span class="n">obj</span><span class="o">.</span><span class="n">db</span><span class="o">.</span><span class="n">test5</span> <span class="o">=</span> <span class="p">{</span><span class="s1">&#39;members&#39;</span><span class="p">:</span> <span class="p">[</span><span class="n">obj1</span><span class="p">,</span><span class="n">obj2</span><span class="p">,</span><span class="n">obj3</span><span class="p">],</span> <span class="s1">&#39;enemies&#39;</span><span class="p">:[</span><span class="n">obj4</span><span class="p">,</span><span class="n">obj5</span><span class="p">]}</span>
<span class="c1"># a tuple with a list in it</span>
<span class="n">obj</span><span class="o">.</span><span class="n">db</span><span class="o">.</span><span class="n">test6</span> <span class="o">=</span> <span class="p">(</span><span class="mi">1</span><span class="p">,</span><span class="mi">3</span><span class="p">,</span><span class="mi">4</span><span class="p">,</span><span class="mi">8</span><span class="p">,</span> <span class="p">[</span><span class="s2">&quot;test&quot;</span><span class="p">,</span> <span class="s2">&quot;test2&quot;</span><span class="p">],</span> <span class="mi">9</span><span class="p">)</span>
<span class="c1"># a set</span>
<span class="n">obj</span><span class="o">.</span><span class="n">db</span><span class="o">.</span><span class="n">test7</span> <span class="o">=</span> <span class="nb">set</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="mi">4</span><span class="p">,</span><span class="mi">5</span><span class="p">])</span>
<span class="c1"># in-situ manipulation</span>
<span class="n">obj</span><span class="o">.</span><span class="n">db</span><span class="o">.</span><span class="n">test8</span> <span class="o">=</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="s2">&quot;test&quot;</span><span class="p">:</span><span class="mi">1</span><span class="p">}]</span>
<span class="n">obj</span><span class="o">.</span><span class="n">db</span><span class="o">.</span><span class="n">test8</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">=</span> <span class="mi">4</span>
<span class="n">obj</span><span class="o">.</span><span class="n">db</span><span class="o">.</span><span class="n">test8</span><span class="p">[</span><span class="mi">2</span><span class="p">][</span><span class="s2">&quot;test&quot;</span><span class="p">]</span> <span class="o">=</span> <span class="mi">5</span>
<span class="c1"># test8 is now [4,2,{&quot;test&quot;:5}]</span>
</pre></div>
</td></tr></table></div>
</section>
<section id="retrieving-mutable-objects">
<h3>Retrieving Mutable objects<a class="headerlink" href="#retrieving-mutable-objects" title="Permalink to this headline"></a></h3>
<p>A side effect of the way Evennia stores Attributes is that <em>mutable</em> iterables (iterables that can
be modified in-place after they were created, which is everything except tuples) are handled by
custom objects called <code class="docutils literal notranslate"><span class="pre">_SaverList</span></code>, <code class="docutils literal notranslate"><span class="pre">_SaverDict</span></code> etc. These <code class="docutils literal notranslate"><span class="pre">_Saver...</span></code> classes behave just like the
normal variant except that they are aware of the database and saves to it whenever new data gets
assigned to them. This is what allows you to do things like <code class="docutils literal notranslate"><span class="pre">self.db.mylist[7]</span> <span class="pre">=</span> <span class="pre">val</span></code> and be sure
that the new version of list is saved. Without this you would have to load the list into a temporary
variable, change it and then re-assign it to the Attribute in order for it to save.</p>
<p>There is however an important thing to remember. If you retrieve your mutable iterable into another
variable, e.g. <code class="docutils literal notranslate"><span class="pre">mylist2</span> <span class="pre">=</span> <span class="pre">obj.db.mylist</span></code>, your new variable (<code class="docutils literal notranslate"><span class="pre">mylist2</span></code>) will <em>still</em> be a
<code class="docutils literal notranslate"><span class="pre">_SaverList</span></code>. This means it will continue to save itself to the database whenever it is updated!</p>
<div class="highlight-python notranslate"><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></pre></div></td><td class="code"><div class="highlight"><pre><span></span> <span class="n">obj</span><span class="o">.</span><span class="n">db</span><span class="o">.</span><span class="n">mylist</span> <span class="o">=</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="mi">4</span><span class="p">]</span>
<span class="n">mylist</span> <span class="o">=</span> <span class="n">obj</span><span class="o">.</span><span class="n">db</span><span class="o">.</span><span class="n">mylist</span>
<span class="n">mylist</span><span class="p">[</span><span class="mi">3</span><span class="p">]</span> <span class="o">=</span> <span class="mi">5</span> <span class="c1"># this will also update database</span>
<span class="nb">print</span><span class="p">(</span><span class="n">mylist</span><span class="p">)</span> <span class="c1"># this is now [1,2,3,5]</span>
<span class="nb">print</span><span class="p">(</span><span class="n">obj</span><span class="o">.</span><span class="n">db</span><span class="o">.</span><span class="n">mylist</span><span class="p">)</span> <span class="c1"># this is also [1,2,3,5]</span>
</pre></div>
</td></tr></table></div>
<p>To “disconnect” your extracted mutable variable from the database you simply need to convert the
<code class="docutils literal notranslate"><span class="pre">_Saver...</span></code> iterable to a normal Python structure. So to convert a <code class="docutils literal notranslate"><span class="pre">_SaverList</span></code>, you use the
<code class="docutils literal notranslate"><span class="pre">list()</span></code> function, for a <code class="docutils literal notranslate"><span class="pre">_SaverDict</span></code> you use <code class="docutils literal notranslate"><span class="pre">dict()</span></code> and so on.</p>
<div class="highlight-python notranslate"><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></pre></div></td><td class="code"><div class="highlight"><pre><span></span> <span class="n">obj</span><span class="o">.</span><span class="n">db</span><span class="o">.</span><span class="n">mylist</span> <span class="o">=</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="mi">4</span><span class="p">]</span>
<span class="n">mylist</span> <span class="o">=</span> <span class="nb">list</span><span class="p">(</span><span class="n">obj</span><span class="o">.</span><span class="n">db</span><span class="o">.</span><span class="n">mylist</span><span class="p">)</span> <span class="c1"># convert to normal list</span>
<span class="n">mylist</span><span class="p">[</span><span class="mi">3</span><span class="p">]</span> <span class="o">=</span> <span class="mi">5</span>
<span class="nb">print</span><span class="p">(</span><span class="n">mylist</span><span class="p">)</span> <span class="c1"># this is now [1,2,3,5]</span>
<span class="nb">print</span><span class="p">(</span><span class="n">obj</span><span class="o">.</span><span class="n">db</span><span class="o">.</span><span class="n">mylist</span><span class="p">)</span> <span class="c1"># this is still [1,2,3,4]</span>
</pre></div>
</td></tr></table></div>
<p>A further problem comes with <em>nested mutables</em>, like a dict containing lists of dicts or something
like that. Each of these nested mutables would be <code class="docutils literal notranslate"><span class="pre">_Saver*</span></code> structures connected to the database and
disconnecting the outermost one of them would not disconnect those nested within. To make really
sure you disonnect a nested structure entirely from the database, Evennia provides a special
function <code class="docutils literal notranslate"><span class="pre">evennia.utils.dbserialize.deserialize</span></code>:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="kn">from</span> <span class="nn">evennia.utils.dbserialize</span> <span class="kn">import</span> <span class="n">deserialize</span>
<span class="n">decoupled_mutables</span> <span class="o">=</span> <span class="n">deserialize</span><span class="p">(</span><span class="n">nested_mutables</span><span class="p">)</span>
</pre></div>
</div>
<p>The result of this operation will be a structure only consisting of normal Python mutables (<code class="docutils literal notranslate"><span class="pre">list</span></code>
instead of <code class="docutils literal notranslate"><span class="pre">_SaverList</span></code> and so on).</p>
<p>Remember, this is only valid for <em>mutable</em> iterables.
<a class="reference external" href="http://en.wikipedia.org/wiki/Immutable">Immutable</a> objects (strings, numbers, tuples etc) are
already disconnected from the database from the onset.</p>
<div class="highlight-python notranslate"><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></pre></div></td><td class="code"><div class="highlight"><pre><span></span> <span class="n">obj</span><span class="o">.</span><span class="n">db</span><span class="o">.</span><span class="n">mytup</span> <span class="o">=</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="mi">4</span><span class="p">])</span>
<span class="n">obj</span><span class="o">.</span><span class="n">db</span><span class="o">.</span><span class="n">mytup</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">=</span> <span class="mi">5</span> <span class="c1"># this fails since tuples are immutable</span>
<span class="c1"># this works but will NOT update database since outermost is a tuple</span>
<span class="n">obj</span><span class="o">.</span><span class="n">db</span><span class="o">.</span><span class="n">mytup</span><span class="p">[</span><span class="mi">2</span><span class="p">][</span><span class="mi">1</span><span class="p">]</span> <span class="o">=</span> <span class="mi">5</span>
<span class="nb">print</span><span class="p">(</span><span class="n">obj</span><span class="o">.</span><span class="n">db</span><span class="o">.</span><span class="n">mytup</span><span class="p">[</span><span class="mi">2</span><span class="p">][</span><span class="mi">1</span><span class="p">])</span> <span class="c1"># this still returns 4, not 5</span>
<span class="n">mytup1</span> <span class="o">=</span> <span class="n">obj</span><span class="o">.</span><span class="n">db</span><span class="o">.</span><span class="n">mytup</span> <span class="c1"># mytup1 is already disconnected from database since outermost</span>
<span class="c1"># iterable is a tuple, so we can edit the internal list as we want</span>
<span class="c1"># without affecting the database.</span>
</pre></div>
</td></tr></table></div>
<blockquote>
<div><p>Attributes will fetch data fresh from the database whenever you read them, so
if you are performing big operations on a mutable Attribute property (such as looping over a list
or dict) you should make sure to “disconnect” the Attributes value first and operate on this
rather than on the Attribute. You can gain dramatic speed improvements to big loops this
way.</p>
</div></blockquote>
</section>
</section>
<section id="locking-and-checking-attributes">
<h2>Locking and checking Attributes<a class="headerlink" href="#locking-and-checking-attributes" title="Permalink to this headline"></a></h2>
<p>Attributes are normally not locked down by default, but you can easily change that for individual
Attributes (like those that may be game-sensitive in games with user-level building).</p>
<p>First you need to set a <em>lock string</em> on your Attribute. Lock strings are specified <a class="reference internal" href="Locks.html"><span class="doc">Locks</span></a>.
The relevant lock types are</p>
<ul class="simple">
<li><p><code class="docutils literal notranslate"><span class="pre">attrread</span></code> - limits who may read the value of the Attribute</p></li>
<li><p><code class="docutils literal notranslate"><span class="pre">attredit</span></code> - limits who may set/change this Attribute</p></li>
</ul>
<p>You cannot use the <code class="docutils literal notranslate"><span class="pre">db</span></code> handler to modify Attribute object (such as setting a lock on them) - The
<code class="docutils literal notranslate"><span class="pre">db</span></code> handler will return the Attributes <em>value</em>, not the Attribute object itself. Instead you use
the AttributeHandler and set it to return the object instead of the value:</p>
<div class="highlight-python notranslate"><table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre><span class="normal">1</span>
<span class="normal">2</span></pre></div></td><td class="code"><div class="highlight"><pre><span></span> <span class="n">lockstring</span> <span class="o">=</span> <span class="s2">&quot;attread:all();attredit:perm(Admins)&quot;</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;myattr&quot;</span><span class="p">,</span> <span class="n">return_obj</span><span class="o">=</span><span class="kc">True</span><span class="p">)</span><span class="o">.</span><span class="n">locks</span><span class="o">.</span><span class="n">add</span><span class="p">(</span><span class="n">lockstring</span><span class="p">)</span>
</pre></div>
</td></tr></table></div>
<p>Note the <code class="docutils literal notranslate"><span class="pre">return_obj</span></code> keyword which makes sure to return the <code class="docutils literal notranslate"><span class="pre">Attribute</span></code> object so its LockHandler
could be accessed.</p>
<p>A lock is no good if nothing checks it and by default Evennia does not check locks on Attributes.
You have to add a check to your commands/code wherever it fits (such as before setting an
Attribute).</p>
<div class="highlight-python notranslate"><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></pre></div></td><td class="code"><div class="highlight"><pre><span></span> <span class="c1"># in some command code where we want to limit</span>
<span class="c1"># setting of a given attribute name on an object</span>
<span class="n">attr</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="n">attrname</span><span class="p">,</span>
<span class="n">return_obj</span><span class="o">=</span><span class="kc">True</span><span class="p">,</span>
<span class="n">accessing_obj</span><span class="o">=</span><span class="n">caller</span><span class="p">,</span>
<span class="n">default</span><span class="o">=</span><span class="kc">None</span><span class="p">,</span>
<span class="n">default_access</span><span class="o">=</span><span class="kc">False</span><span class="p">)</span>
<span class="k">if</span> <span class="ow">not</span> <span class="n">attr</span><span class="p">:</span>
<span class="n">caller</span><span class="o">.</span><span class="n">msg</span><span class="p">(</span><span class="s2">&quot;You cannot edit that Attribute!&quot;</span><span class="p">)</span>
<span class="k">return</span>
<span class="c1"># edit the Attribute here</span>
</pre></div>
</td></tr></table></div>
<p>The same keywords are available to use with <code class="docutils literal notranslate"><span class="pre">obj.attributes.set()</span></code> and <code class="docutils literal notranslate"><span class="pre">obj.attributes.remove()</span></code>,
those will check for the <code class="docutils literal notranslate"><span class="pre">attredit</span></code> lock type.</p>
</section>
</section>
<div class="clearer"></div>
</div>
</div>
</div>
<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>
<p><h3><a href="../index.html">Table of Contents</a></h3>
<ul>
<li><a class="reference internal" href="#">Attributes</a><ul>
<li><a class="reference internal" href="#the-db-and-ndb-shortcuts">The .db and .ndb shortcuts</a></li>
<li><a class="reference internal" href="#the-attributehandler">The AttributeHandler</a></li>
<li><a class="reference internal" href="#properties-of-attributes">Properties of Attributes</a></li>
<li><a class="reference internal" href="#persistent-vs-non-persistent">Persistent vs non-persistent</a></li>
<li><a class="reference internal" href="#what-types-of-data-can-i-save-in-an-attribute">What types of data can I save in an Attribute?</a><ul>
<li><a class="reference internal" href="#storing-single-objects">Storing single objects</a></li>
<li><a class="reference internal" href="#storing-multiple-objects">Storing multiple objects</a></li>
<li><a class="reference internal" href="#retrieving-mutable-objects">Retrieving Mutable objects</a></li>
</ul>
</li>
<li><a class="reference internal" href="#locking-and-checking-attributes">Locking and checking Attributes</a></li>
</ul>
</li>
</ul>
<div role="note" aria-label="source link">
<!--h3>This Page</h3-->
<ul class="this-page-menu">
<li><a href="../_sources/Components/Attributes.md.txt"
rel="nofollow">Show Page Source</a></li>
</ul>
</div><h3>Links</h3>
<ul>
<li><a href="https://www.evennia.com">Home page</a> </li>
<li><a href="https://github.com/evennia/evennia">Evennia Github</a> </li>
<li><a href="http://games.evennia.com">Game Index</a> </li>
<li><a href="http://webchat.freenode.net/?channels=evennia&uio=MT1mYWxzZSY5PXRydWUmMTE9MTk1JjEyPXRydWUbb">IRC</a> -
<a href="https://discord.gg/NecFePw">Discord</a> -
<a href="https://groups.google.com/forum/#%21forum/evennia">Forums</a>
</li>
<li><a href="http://evennia.blogspot.com/">Evennia Dev blog</a> </li>
</ul>
<h3>Versions</h3>
<ul>
<li><a href="Attributes.html">1.0-dev (develop branch)</a></li>
<li><a href="../../0.9.5/index.html">0.9.5 (v0.9.5 branch)</a></li>
</ul>
</div>
</div>
<div class="clearer"></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="nav-item nav-item-0"><a href="../index.html">Evennia 1.0-dev</a> &#187;</li>
<li class="nav-item nav-item-this"><a href="">Attributes</a></li>
</ul>
</div>
<div class="footer" role="contentinfo">
&#169; Copyright 2020, The Evennia developer community.
Created using <a href="https://www.sphinx-doc.org/">Sphinx</a> 3.2.1.
</div>
</body>
</html>