evennia/docs/latest/_modules/evennia/objects/models.html
Evennia docbuilder action ce899c6430 Updated HTML docs.
2024-08-11 18:07:20 +00:00

525 lines
No EOL
48 KiB
HTML

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>evennia.objects.models &#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" />
</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 latest</a> &#187;</li>
<li class="nav-item nav-item-1"><a href="../../index.html" >Module code</a> &#187;</li>
<li class="nav-item nav-item-2"><a href="../../evennia.html" accesskey="U">evennia</a> &#187;</li>
<li class="nav-item nav-item-this"><a href="">evennia.objects.models</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>Links</h3>
<ul>
<li><a href="https://www.evennia.com/docs/latest/index.html">Documentation Top</a> </li>
<li><a href="https://www.evennia.com">Evennia Home</a> </li>
<li><a href="https://github.com/evennia/evennia">Github</a> </li>
<li><a href="http://games.evennia.com">Game Index</a> </li>
<li>
<a href="https://discord.gg/AJJpcRUhtF">Discord</a> -
<a href="https://github.com/evennia/evennia/discussions">Discussions</a> -
<a href="https://evennia.blogspot.com/">Blog</a>
</li>
</ul>
<h3>Doc Versions</h3>
<ul>
<li><a href="models.html">latest (main branch)</a></li>
<li><a href="../4.x/index.html">v4.0.0 branch (outdated)</a></li>
<li><a href="../3.x/index.html">v3.0.0 branch (outdated)</a></li>
<li><a href="../2.x/index.html">v2.0.0 branch (outdated)</a></li>
<li><a href="../1.x/index.html">v1.0.0 branch (outdated)</a></li>
<li><a href="../0.x/index.html">v0.9.5 branch (outdated)</a></li>
</ul>
</div>
</div>
<div class="bodywrapper">
<div class="body" role="main">
<h1>Source code for evennia.objects.models</h1><div class="highlight"><pre>
<span></span><span class="sd">&quot;&quot;&quot;</span>
<span class="sd">This module defines the database models for all in-game objects, that</span>
<span class="sd">is, all objects that has an actual existence in-game.</span>
<span class="sd">Each database object is &#39;decorated&#39; with a &#39;typeclass&#39;, a normal</span>
<span class="sd">python class that implements all the various logics needed by the game</span>
<span class="sd">in question. Objects created of this class transparently communicate</span>
<span class="sd">with its related database object for storing all attributes. The</span>
<span class="sd">admin should usually not have to deal directly with this database</span>
<span class="sd">object layer.</span>
<span class="sd">Attributes are separate objects that store values persistently onto</span>
<span class="sd">the database object. Like everything else, they can be accessed</span>
<span class="sd">transparently through the decorating TypeClass.</span>
<span class="sd">&quot;&quot;&quot;</span>
<span class="kn">from</span> <span class="nn">collections</span> <span class="kn">import</span> <span class="n">defaultdict</span>
<span class="kn">from</span> <span class="nn">django.conf</span> <span class="kn">import</span> <span class="n">settings</span>
<span class="kn">from</span> <span class="nn">django.core.exceptions</span> <span class="kn">import</span> <span class="n">ObjectDoesNotExist</span>
<span class="kn">from</span> <span class="nn">django.core.validators</span> <span class="kn">import</span> <span class="n">validate_comma_separated_integer_list</span>
<span class="kn">from</span> <span class="nn">django.db</span> <span class="kn">import</span> <span class="n">models</span>
<span class="kn">from</span> <span class="nn">evennia.objects.manager</span> <span class="kn">import</span> <span class="n">ObjectDBManager</span>
<span class="kn">from</span> <span class="nn">evennia.typeclasses.models</span> <span class="kn">import</span> <span class="n">TypedObject</span>
<span class="kn">from</span> <span class="nn">evennia.utils</span> <span class="kn">import</span> <span class="n">logger</span>
<span class="kn">from</span> <span class="nn">evennia.utils.utils</span> <span class="kn">import</span> <span class="n">dbref</span><span class="p">,</span> <span class="n">lazy_property</span><span class="p">,</span> <span class="n">make_iter</span>
<div class="viewcode-block" id="ContentsHandler"><a class="viewcode-back" href="../../../api/evennia.objects.models.html#evennia.objects.models.ContentsHandler">[docs]</a><span class="k">class</span> <span class="nc">ContentsHandler</span><span class="p">:</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;</span>
<span class="sd"> Handles and caches the contents of an object to avoid excessive</span>
<span class="sd"> lookups (this is done very often due to cmdhandler needing to look</span>
<span class="sd"> for object-cmdsets). It is stored on the &#39;contents_cache&#39; property</span>
<span class="sd"> of the ObjectDB.</span>
<span class="sd"> &quot;&quot;&quot;</span>
<div class="viewcode-block" id="ContentsHandler.__init__"><a class="viewcode-back" href="../../../api/evennia.objects.models.html#evennia.objects.models.ContentsHandler.__init__">[docs]</a> <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="w"> </span><span class="sd">&quot;&quot;&quot;</span>
<span class="sd"> Sets up the contents handler.</span>
<span class="sd"> Args:</span>
<span class="sd"> obj (Object): The object on which the</span>
<span class="sd"> handler is defined</span>
<span class="sd"> Notes:</span>
<span class="sd"> This was changed from using `set` to using `dict` internally</span>
<span class="sd"> in order to retain insertion order.</span>
<span class="sd"> &quot;&quot;&quot;</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">_pkcache</span> <span class="o">=</span> <span class="p">{}</span>
<span class="bp">self</span><span class="o">.</span><span class="n">_idcache</span> <span class="o">=</span> <span class="n">obj</span><span class="o">.</span><span class="vm">__class__</span><span class="o">.</span><span class="n">__instance_cache__</span>
<span class="bp">self</span><span class="o">.</span><span class="n">_typecache</span> <span class="o">=</span> <span class="n">defaultdict</span><span class="p">(</span><span class="nb">dict</span><span class="p">)</span>
<span class="bp">self</span><span class="o">.</span><span class="n">init</span><span class="p">()</span></div>
<div class="viewcode-block" id="ContentsHandler.load"><a class="viewcode-back" href="../../../api/evennia.objects.models.html#evennia.objects.models.ContentsHandler.load">[docs]</a> <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="w"> </span><span class="sd">&quot;&quot;&quot;</span>
<span class="sd"> Retrieves all objects from database. Used for initializing.</span>
<span class="sd"> Returns:</span>
<span class="sd"> Objects (list of ObjectDB)</span>
<span class="sd"> &quot;&quot;&quot;</span>
<span class="k">return</span> <span class="nb">list</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">locations_set</span><span class="o">.</span><span class="n">all</span><span class="p">())</span></div>
<div class="viewcode-block" id="ContentsHandler.init"><a class="viewcode-back" href="../../../api/evennia.objects.models.html#evennia.objects.models.ContentsHandler.init">[docs]</a> <span class="k">def</span> <span class="nf">init</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;</span>
<span class="sd"> Re-initialize the content cache</span>
<span class="sd"> &quot;&quot;&quot;</span>
<span class="n">objects</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">load</span><span class="p">()</span>
<span class="bp">self</span><span class="o">.</span><span class="n">_pkcache</span> <span class="o">=</span> <span class="p">{</span><span class="n">obj</span><span class="o">.</span><span class="n">pk</span><span class="p">:</span> <span class="kc">True</span> <span class="k">for</span> <span class="n">obj</span> <span class="ow">in</span> <span class="n">objects</span><span class="p">}</span>
<span class="k">for</span> <span class="n">obj</span> <span class="ow">in</span> <span class="n">objects</span><span class="p">:</span>
<span class="k">try</span><span class="p">:</span>
<span class="n">ctypes</span> <span class="o">=</span> <span class="n">obj</span><span class="o">.</span><span class="n">_content_types</span>
<span class="k">except</span> <span class="ne">AttributeError</span><span class="p">:</span>
<span class="n">logger</span><span class="o">.</span><span class="n">log_err</span><span class="p">(</span>
<span class="sa">f</span><span class="s2">&quot;Object </span><span class="si">{</span><span class="n">obj</span><span class="si">}</span><span class="s2"> has no `_content_types` property. Skipping content-cache setup. &quot;</span>
<span class="s2">&quot;This error suggests it is not a valid Evennia Typeclass but maybe a root model &quot;</span>
<span class="s2">&quot;like `ObjectDB`. Investigate the `db_typeclass_path` of the object and make sure &quot;</span>
<span class="s2">&quot;it points to a proper, existing Typeclass.&quot;</span>
<span class="p">)</span>
<span class="k">else</span><span class="p">:</span>
<span class="k">for</span> <span class="n">ctype</span> <span class="ow">in</span> <span class="n">obj</span><span class="o">.</span><span class="n">_content_types</span><span class="p">:</span>
<span class="bp">self</span><span class="o">.</span><span class="n">_typecache</span><span class="p">[</span><span class="n">ctype</span><span class="p">][</span><span class="n">obj</span><span class="o">.</span><span class="n">pk</span><span class="p">]</span> <span class="o">=</span> <span class="kc">True</span></div>
<div class="viewcode-block" id="ContentsHandler.get"><a class="viewcode-back" href="../../../api/evennia.objects.models.html#evennia.objects.models.ContentsHandler.get">[docs]</a> <span class="k">def</span> <span class="nf">get</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">exclude</span><span class="o">=</span><span class="kc">None</span><span class="p">,</span> <span class="n">content_type</span><span class="o">=</span><span class="kc">None</span><span class="p">):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;</span>
<span class="sd"> Return the contents of the cache.</span>
<span class="sd"> Args:</span>
<span class="sd"> exclude (Object or list of Object): object(s) to ignore</span>
<span class="sd"> content_type (str or None): Filter list by a content-type. If None, don&#39;t filter.</span>
<span class="sd"> Returns:</span>
<span class="sd"> objects (list): the Objects inside this location</span>
<span class="sd"> &quot;&quot;&quot;</span>
<span class="k">if</span> <span class="n">content_type</span> <span class="ow">is</span> <span class="ow">not</span> <span class="kc">None</span><span class="p">:</span>
<span class="n">pks</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">_typecache</span><span class="p">[</span><span class="n">content_type</span><span class="p">]</span><span class="o">.</span><span class="n">keys</span><span class="p">()</span>
<span class="k">else</span><span class="p">:</span>
<span class="n">pks</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">_pkcache</span><span class="o">.</span><span class="n">keys</span><span class="p">()</span>
<span class="k">if</span> <span class="n">exclude</span><span class="p">:</span>
<span class="n">pks</span> <span class="o">=</span> <span class="nb">set</span><span class="p">(</span><span class="n">pks</span><span class="p">)</span> <span class="o">-</span> <span class="p">{</span><span class="n">excl</span><span class="o">.</span><span class="n">pk</span> <span class="k">for</span> <span class="n">excl</span> <span class="ow">in</span> <span class="n">make_iter</span><span class="p">(</span><span class="n">exclude</span><span class="p">)}</span>
<span class="k">try</span><span class="p">:</span>
<span class="k">return</span> <span class="p">[</span><span class="bp">self</span><span class="o">.</span><span class="n">_idcache</span><span class="p">[</span><span class="n">pk</span><span class="p">]</span> <span class="k">for</span> <span class="n">pk</span> <span class="ow">in</span> <span class="n">pks</span><span class="p">]</span>
<span class="k">except</span> <span class="ne">KeyError</span><span class="p">:</span>
<span class="c1"># this can happen if the idmapper cache was cleared for an object</span>
<span class="c1"># in the contents cache. If so we need to re-initialize and try again.</span>
<span class="bp">self</span><span class="o">.</span><span class="n">init</span><span class="p">()</span>
<span class="k">try</span><span class="p">:</span>
<span class="k">return</span> <span class="p">[</span><span class="bp">self</span><span class="o">.</span><span class="n">_idcache</span><span class="p">[</span><span class="n">pk</span><span class="p">]</span> <span class="k">for</span> <span class="n">pk</span> <span class="ow">in</span> <span class="n">pks</span><span class="p">]</span>
<span class="k">except</span> <span class="ne">KeyError</span><span class="p">:</span>
<span class="c1"># this means an actual failure of caching. Return real database match.</span>
<span class="n">logger</span><span class="o">.</span><span class="n">log_err</span><span class="p">(</span><span class="s2">&quot;contents cache failed for </span><span class="si">%s</span><span class="s2">.&quot;</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">key</span><span class="p">)</span>
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">load</span><span class="p">()</span></div>
<div class="viewcode-block" id="ContentsHandler.add"><a class="viewcode-back" href="../../../api/evennia.objects.models.html#evennia.objects.models.ContentsHandler.add">[docs]</a> <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">obj</span><span class="p">):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;</span>
<span class="sd"> Add a new object to this location</span>
<span class="sd"> Args:</span>
<span class="sd"> obj (Object): object to add</span>
<span class="sd"> &quot;&quot;&quot;</span>
<span class="bp">self</span><span class="o">.</span><span class="n">_pkcache</span><span class="p">[</span><span class="n">obj</span><span class="o">.</span><span class="n">pk</span><span class="p">]</span> <span class="o">=</span> <span class="n">obj</span>
<span class="k">for</span> <span class="n">ctype</span> <span class="ow">in</span> <span class="n">obj</span><span class="o">.</span><span class="n">_content_types</span><span class="p">:</span>
<span class="bp">self</span><span class="o">.</span><span class="n">_typecache</span><span class="p">[</span><span class="n">ctype</span><span class="p">][</span><span class="n">obj</span><span class="o">.</span><span class="n">pk</span><span class="p">]</span> <span class="o">=</span> <span class="kc">True</span></div>
<div class="viewcode-block" id="ContentsHandler.remove"><a class="viewcode-back" href="../../../api/evennia.objects.models.html#evennia.objects.models.ContentsHandler.remove">[docs]</a> <span class="k">def</span> <span class="nf">remove</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="w"> </span><span class="sd">&quot;&quot;&quot;</span>
<span class="sd"> Remove object from this location</span>
<span class="sd"> Args:</span>
<span class="sd"> obj (Object): object to remove</span>
<span class="sd"> &quot;&quot;&quot;</span>
<span class="bp">self</span><span class="o">.</span><span class="n">_pkcache</span><span class="o">.</span><span class="n">pop</span><span class="p">(</span><span class="n">obj</span><span class="o">.</span><span class="n">pk</span><span class="p">,</span> <span class="kc">None</span><span class="p">)</span>
<span class="k">for</span> <span class="n">ctype</span> <span class="ow">in</span> <span class="n">obj</span><span class="o">.</span><span class="n">_content_types</span><span class="p">:</span>
<span class="k">if</span> <span class="n">obj</span><span class="o">.</span><span class="n">pk</span> <span class="ow">in</span> <span class="bp">self</span><span class="o">.</span><span class="n">_typecache</span><span class="p">[</span><span class="n">ctype</span><span class="p">]:</span>
<span class="bp">self</span><span class="o">.</span><span class="n">_typecache</span><span class="p">[</span><span class="n">ctype</span><span class="p">]</span><span class="o">.</span><span class="n">pop</span><span class="p">(</span><span class="n">obj</span><span class="o">.</span><span class="n">pk</span><span class="p">,</span> <span class="kc">None</span><span class="p">)</span></div>
<div class="viewcode-block" id="ContentsHandler.clear"><a class="viewcode-back" href="../../../api/evennia.objects.models.html#evennia.objects.models.ContentsHandler.clear">[docs]</a> <span class="k">def</span> <span class="nf">clear</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;</span>
<span class="sd"> Clear the contents cache and re-initialize</span>
<span class="sd"> &quot;&quot;&quot;</span>
<span class="bp">self</span><span class="o">.</span><span class="n">_pkcache</span> <span class="o">=</span> <span class="p">{}</span>
<span class="bp">self</span><span class="o">.</span><span class="n">_typecache</span> <span class="o">=</span> <span class="n">defaultdict</span><span class="p">(</span><span class="nb">dict</span><span class="p">)</span>
<span class="bp">self</span><span class="o">.</span><span class="n">init</span><span class="p">()</span></div></div>
<span class="c1"># -------------------------------------------------------------</span>
<span class="c1">#</span>
<span class="c1"># ObjectDB</span>
<span class="c1">#</span>
<span class="c1"># -------------------------------------------------------------</span>
<div class="viewcode-block" id="ObjectDB"><a class="viewcode-back" href="../../../api/evennia.objects.models.html#evennia.objects.models.ObjectDB">[docs]</a><span class="k">class</span> <span class="nc">ObjectDB</span><span class="p">(</span><span class="n">TypedObject</span><span class="p">):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;</span>
<span class="sd"> All objects in the game use the ObjectDB model to store</span>
<span class="sd"> data in the database. This is handled transparently through</span>
<span class="sd"> the typeclass system.</span>
<span class="sd"> Note that the base objectdb is very simple, with</span>
<span class="sd"> few defined fields. Use attributes to extend your</span>
<span class="sd"> type class with new database-stored variables.</span>
<span class="sd"> The TypedObject supplies the following (inherited) properties:</span>
<span class="sd"> - key - main name</span>
<span class="sd"> - name - alias for key</span>
<span class="sd"> - db_typeclass_path - the path to the decorating typeclass</span>
<span class="sd"> - db_date_created - time stamp of object creation</span>
<span class="sd"> - permissions - perm strings</span>
<span class="sd"> - locks - lock definitions (handler)</span>
<span class="sd"> - dbref - #id of object</span>
<span class="sd"> - db - persistent attribute storage</span>
<span class="sd"> - ndb - non-persistent attribute storage</span>
<span class="sd"> The ObjectDB adds the following properties:</span>
<span class="sd"> - account - optional connected account (always together with sessid)</span>
<span class="sd"> - sessid - optional connection session id (always together with account)</span>
<span class="sd"> - location - in-game location of object</span>
<span class="sd"> - home - safety location for object (handler)</span>
<span class="sd"> - scripts - scripts assigned to object (handler from typeclass)</span>
<span class="sd"> - cmdset - active cmdset on object (handler from typeclass)</span>
<span class="sd"> - aliases - aliases for this object (property)</span>
<span class="sd"> - nicks - nicknames for *other* things in Evennia (handler)</span>
<span class="sd"> - sessions - sessions connected to this object (see also account)</span>
<span class="sd"> - has_account - bool if an active account is currently connected</span>
<span class="sd"> - contents - other objects having this object as location</span>
<span class="sd"> - exits - exits from this object</span>
<span class="sd"> &quot;&quot;&quot;</span>
<span class="c1">#</span>
<span class="c1"># ObjectDB Database model setup</span>
<span class="c1">#</span>
<span class="c1">#</span>
<span class="c1"># inherited fields (from TypedObject):</span>
<span class="c1"># db_key (also &#39;name&#39; works), db_typeclass_path, db_date_created,</span>
<span class="c1"># db_permissions</span>
<span class="c1">#</span>
<span class="c1"># These databse fields (including the inherited ones) should normally be</span>
<span class="c1"># managed by their corresponding wrapper properties, named same as the</span>
<span class="c1"># field, but without the db_* prefix (e.g. the db_key field is set with</span>
<span class="c1"># self.key instead). The wrappers are created at the metaclass level and</span>
<span class="c1"># will automatically save and cache the data more efficiently.</span>
<span class="c1"># If this is a character object, the account is connected here.</span>
<span class="n">db_account</span> <span class="o">=</span> <span class="n">models</span><span class="o">.</span><span class="n">ForeignKey</span><span class="p">(</span>
<span class="s2">&quot;accounts.AccountDB&quot;</span><span class="p">,</span>
<span class="n">null</span><span class="o">=</span><span class="kc">True</span><span class="p">,</span>
<span class="n">verbose_name</span><span class="o">=</span><span class="s2">&quot;account&quot;</span><span class="p">,</span>
<span class="n">on_delete</span><span class="o">=</span><span class="n">models</span><span class="o">.</span><span class="n">SET_NULL</span><span class="p">,</span>
<span class="n">help_text</span><span class="o">=</span><span class="s2">&quot;an Account connected to this object, if any.&quot;</span><span class="p">,</span>
<span class="p">)</span>
<span class="c1"># the session id associated with this account, if any</span>
<span class="n">db_sessid</span> <span class="o">=</span> <span class="n">models</span><span class="o">.</span><span class="n">CharField</span><span class="p">(</span>
<span class="n">null</span><span class="o">=</span><span class="kc">True</span><span class="p">,</span>
<span class="n">max_length</span><span class="o">=</span><span class="mi">32</span><span class="p">,</span>
<span class="n">validators</span><span class="o">=</span><span class="p">[</span><span class="n">validate_comma_separated_integer_list</span><span class="p">],</span>
<span class="n">verbose_name</span><span class="o">=</span><span class="s2">&quot;session id&quot;</span><span class="p">,</span>
<span class="n">help_text</span><span class="o">=</span><span class="s2">&quot;csv list of session ids of connected Account, if any.&quot;</span><span class="p">,</span>
<span class="p">)</span>
<span class="c1"># The location in the game world. Since this one is likely</span>
<span class="c1"># to change often, we set this with the &#39;location&#39; property</span>
<span class="c1"># to transparently handle Typeclassing.</span>
<span class="n">db_location</span> <span class="o">=</span> <span class="n">models</span><span class="o">.</span><span class="n">ForeignKey</span><span class="p">(</span>
<span class="s2">&quot;self&quot;</span><span class="p">,</span>
<span class="n">related_name</span><span class="o">=</span><span class="s2">&quot;locations_set&quot;</span><span class="p">,</span>
<span class="n">db_index</span><span class="o">=</span><span class="kc">True</span><span class="p">,</span>
<span class="n">on_delete</span><span class="o">=</span><span class="n">models</span><span class="o">.</span><span class="n">SET_NULL</span><span class="p">,</span>
<span class="n">blank</span><span class="o">=</span><span class="kc">True</span><span class="p">,</span>
<span class="n">null</span><span class="o">=</span><span class="kc">True</span><span class="p">,</span>
<span class="n">verbose_name</span><span class="o">=</span><span class="s2">&quot;game location&quot;</span><span class="p">,</span>
<span class="p">)</span>
<span class="c1"># a safety location, this usually don&#39;t change much.</span>
<span class="n">db_home</span> <span class="o">=</span> <span class="n">models</span><span class="o">.</span><span class="n">ForeignKey</span><span class="p">(</span>
<span class="s2">&quot;self&quot;</span><span class="p">,</span>
<span class="n">related_name</span><span class="o">=</span><span class="s2">&quot;homes_set&quot;</span><span class="p">,</span>
<span class="n">on_delete</span><span class="o">=</span><span class="n">models</span><span class="o">.</span><span class="n">SET_NULL</span><span class="p">,</span>
<span class="n">blank</span><span class="o">=</span><span class="kc">True</span><span class="p">,</span>
<span class="n">null</span><span class="o">=</span><span class="kc">True</span><span class="p">,</span>
<span class="n">verbose_name</span><span class="o">=</span><span class="s2">&quot;home location&quot;</span><span class="p">,</span>
<span class="p">)</span>
<span class="c1"># destination of this object - primarily used by exits.</span>
<span class="n">db_destination</span> <span class="o">=</span> <span class="n">models</span><span class="o">.</span><span class="n">ForeignKey</span><span class="p">(</span>
<span class="s2">&quot;self&quot;</span><span class="p">,</span>
<span class="n">related_name</span><span class="o">=</span><span class="s2">&quot;destinations_set&quot;</span><span class="p">,</span>
<span class="n">db_index</span><span class="o">=</span><span class="kc">True</span><span class="p">,</span>
<span class="n">on_delete</span><span class="o">=</span><span class="n">models</span><span class="o">.</span><span class="n">SET_NULL</span><span class="p">,</span>
<span class="n">blank</span><span class="o">=</span><span class="kc">True</span><span class="p">,</span>
<span class="n">null</span><span class="o">=</span><span class="kc">True</span><span class="p">,</span>
<span class="n">verbose_name</span><span class="o">=</span><span class="s2">&quot;destination&quot;</span><span class="p">,</span>
<span class="n">help_text</span><span class="o">=</span><span class="s2">&quot;a destination, used only by exit objects.&quot;</span><span class="p">,</span>
<span class="p">)</span>
<span class="c1"># database storage of persistant cmdsets.</span>
<span class="n">db_cmdset_storage</span> <span class="o">=</span> <span class="n">models</span><span class="o">.</span><span class="n">CharField</span><span class="p">(</span>
<span class="s2">&quot;cmdset&quot;</span><span class="p">,</span>
<span class="n">max_length</span><span class="o">=</span><span class="mi">255</span><span class="p">,</span>
<span class="n">null</span><span class="o">=</span><span class="kc">True</span><span class="p">,</span>
<span class="n">blank</span><span class="o">=</span><span class="kc">True</span><span class="p">,</span>
<span class="n">help_text</span><span class="o">=</span><span class="s2">&quot;optional python path to a cmdset class.&quot;</span><span class="p">,</span>
<span class="p">)</span>
<span class="c1"># Database manager</span>
<span class="n">objects</span> <span class="o">=</span> <span class="n">ObjectDBManager</span><span class="p">()</span>
<span class="c1"># defaults</span>
<span class="n">__settingsclasspath__</span> <span class="o">=</span> <span class="n">settings</span><span class="o">.</span><span class="n">BASE_OBJECT_TYPECLASS</span>
<span class="n">__defaultclasspath__</span> <span class="o">=</span> <span class="s2">&quot;evennia.objects.objects.DefaultObject&quot;</span>
<span class="n">__applabel__</span> <span class="o">=</span> <span class="s2">&quot;objects&quot;</span>
<div class="viewcode-block" id="ObjectDB.contents_cache"><a class="viewcode-back" href="../../../api/evennia.objects.models.html#evennia.objects.models.ObjectDB.contents_cache">[docs]</a> <span class="nd">@lazy_property</span>
<span class="k">def</span> <span class="nf">contents_cache</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="k">return</span> <span class="n">ContentsHandler</span><span class="p">(</span><span class="bp">self</span><span class="p">)</span></div>
<span class="c1"># cmdset_storage property handling</span>
<span class="k">def</span> <span class="nf">__cmdset_storage_get</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;getter&quot;&quot;&quot;</span>
<span class="n">storage</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">db_cmdset_storage</span>
<span class="k">return</span> <span class="p">[</span><span class="n">path</span><span class="o">.</span><span class="n">strip</span><span class="p">()</span> <span class="k">for</span> <span class="n">path</span> <span class="ow">in</span> <span class="n">storage</span><span class="o">.</span><span class="n">split</span><span class="p">(</span><span class="s2">&quot;,&quot;</span><span class="p">)]</span> <span class="k">if</span> <span class="n">storage</span> <span class="k">else</span> <span class="p">[]</span>
<span class="k">def</span> <span class="nf">__cmdset_storage_set</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="w"> </span><span class="sd">&quot;&quot;&quot;setter&quot;&quot;&quot;</span>
<span class="bp">self</span><span class="o">.</span><span class="n">db_cmdset_storage</span> <span class="o">=</span> <span class="s2">&quot;,&quot;</span><span class="o">.</span><span class="n">join</span><span class="p">(</span><span class="nb">str</span><span class="p">(</span><span class="n">val</span><span class="p">)</span><span class="o">.</span><span class="n">strip</span><span class="p">()</span> <span class="k">for</span> <span class="n">val</span> <span class="ow">in</span> <span class="n">make_iter</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">save</span><span class="p">(</span><span class="n">update_fields</span><span class="o">=</span><span class="p">[</span><span class="s2">&quot;db_cmdset_storage&quot;</span><span class="p">])</span>
<span class="k">def</span> <span class="nf">__cmdset_storage_del</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;deleter&quot;&quot;&quot;</span>
<span class="bp">self</span><span class="o">.</span><span class="n">db_cmdset_storage</span> <span class="o">=</span> <span class="kc">None</span>
<span class="bp">self</span><span class="o">.</span><span class="n">save</span><span class="p">(</span><span class="n">update_fields</span><span class="o">=</span><span class="p">[</span><span class="s2">&quot;db_cmdset_storage&quot;</span><span class="p">])</span>
<span class="n">cmdset_storage</span> <span class="o">=</span> <span class="nb">property</span><span class="p">(</span><span class="n">__cmdset_storage_get</span><span class="p">,</span> <span class="n">__cmdset_storage_set</span><span class="p">,</span> <span class="n">__cmdset_storage_del</span><span class="p">)</span>
<span class="c1"># location getsetter</span>
<span class="k">def</span> <span class="nf">__location_get</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;Get location&quot;&quot;&quot;</span>
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">db_location</span>
<span class="k">def</span> <span class="nf">__location_set</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">location</span><span class="p">):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;Set location, checking for loops and allowing dbref&quot;&quot;&quot;</span>
<span class="k">if</span> <span class="nb">isinstance</span><span class="p">(</span><span class="n">location</span><span class="p">,</span> <span class="p">(</span><span class="nb">str</span><span class="p">,</span> <span class="nb">int</span><span class="p">)):</span>
<span class="c1"># allow setting of #dbref</span>
<span class="n">dbid</span> <span class="o">=</span> <span class="n">dbref</span><span class="p">(</span><span class="n">location</span><span class="p">,</span> <span class="n">reqhash</span><span class="o">=</span><span class="kc">False</span><span class="p">)</span>
<span class="k">if</span> <span class="n">dbid</span><span class="p">:</span>
<span class="k">try</span><span class="p">:</span>
<span class="n">location</span> <span class="o">=</span> <span class="n">ObjectDB</span><span class="o">.</span><span class="n">objects</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="nb">id</span><span class="o">=</span><span class="n">dbid</span><span class="p">)</span>
<span class="k">except</span> <span class="n">ObjectDoesNotExist</span><span class="p">:</span>
<span class="c1"># maybe it is just a name that happens to look like a dbid</span>
<span class="k">pass</span>
<span class="k">try</span><span class="p">:</span>
<span class="k">def</span> <span class="nf">is_loc_loop</span><span class="p">(</span><span class="n">loc</span><span class="p">,</span> <span class="n">depth</span><span class="o">=</span><span class="mi">0</span><span class="p">):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;Recursively traverse target location, trying to catch a loop.&quot;&quot;&quot;</span>
<span class="k">if</span> <span class="n">depth</span> <span class="o">&gt;</span> <span class="mi">10</span><span class="p">:</span>
<span class="k">return</span> <span class="kc">None</span>
<span class="k">elif</span> <span class="n">loc</span> <span class="o">==</span> <span class="bp">self</span><span class="p">:</span>
<span class="k">raise</span> <span class="ne">RuntimeError</span>
<span class="k">elif</span> <span class="n">loc</span> <span class="ow">is</span> <span class="kc">None</span><span class="p">:</span>
<span class="k">raise</span> <span class="ne">RuntimeWarning</span>
<span class="k">return</span> <span class="n">is_loc_loop</span><span class="p">(</span><span class="n">loc</span><span class="o">.</span><span class="n">db_location</span><span class="p">,</span> <span class="n">depth</span> <span class="o">+</span> <span class="mi">1</span><span class="p">)</span>
<span class="k">try</span><span class="p">:</span>
<span class="n">is_loc_loop</span><span class="p">(</span><span class="n">location</span><span class="p">)</span>
<span class="k">except</span> <span class="ne">RuntimeWarning</span><span class="p">:</span>
<span class="c1"># we caught an infinite location loop!</span>
<span class="c1"># (location1 is in location2 which is in location1 ...)</span>
<span class="k">pass</span>
<span class="c1"># if we get to this point we are ready to change location</span>
<span class="n">old_location</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">db_location</span>
<span class="c1"># this is checked in _db_db_location_post_save below</span>
<span class="bp">self</span><span class="o">.</span><span class="n">_safe_contents_update</span> <span class="o">=</span> <span class="kc">True</span>
<span class="c1"># actually set the field (this will error if location is invalid)</span>
<span class="bp">self</span><span class="o">.</span><span class="n">db_location</span> <span class="o">=</span> <span class="n">location</span>
<span class="bp">self</span><span class="o">.</span><span class="n">save</span><span class="p">(</span><span class="n">update_fields</span><span class="o">=</span><span class="p">[</span><span class="s2">&quot;db_location&quot;</span><span class="p">])</span>
<span class="c1"># remove the safe flag</span>
<span class="k">del</span> <span class="bp">self</span><span class="o">.</span><span class="n">_safe_contents_update</span>
<span class="c1"># update the contents cache</span>
<span class="k">if</span> <span class="n">old_location</span><span class="p">:</span>
<span class="n">old_location</span><span class="o">.</span><span class="n">contents_cache</span><span class="o">.</span><span class="n">remove</span><span class="p">(</span><span class="bp">self</span><span class="p">)</span>
<span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">db_location</span><span class="p">:</span>
<span class="bp">self</span><span class="o">.</span><span class="n">db_location</span><span class="o">.</span><span class="n">contents_cache</span><span class="o">.</span><span class="n">add</span><span class="p">(</span><span class="bp">self</span><span class="p">)</span>
<span class="k">except</span> <span class="ne">RuntimeError</span><span class="p">:</span>
<span class="n">errmsg</span> <span class="o">=</span> <span class="s2">&quot;Error: </span><span class="si">%s</span><span class="s2">.location = </span><span class="si">%s</span><span class="s2"> creates a location loop.&quot;</span> <span class="o">%</span> <span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">key</span><span class="p">,</span> <span class="n">location</span><span class="p">)</span>
<span class="k">raise</span> <span class="ne">RuntimeError</span><span class="p">(</span><span class="n">errmsg</span><span class="p">)</span>
<span class="k">except</span> <span class="ne">Exception</span><span class="p">:</span>
<span class="c1"># raising here gives more info for now</span>
<span class="k">raise</span>
<span class="c1"># errmsg = &quot;Error (%s): %s is not a valid location.&quot; % (str(e), location)</span>
<span class="c1"># raise RuntimeError(errmsg)</span>
<span class="k">return</span>
<span class="k">def</span> <span class="nf">__location_del</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;Cleanly delete the location reference&quot;&quot;&quot;</span>
<span class="bp">self</span><span class="o">.</span><span class="n">db_location</span> <span class="o">=</span> <span class="kc">None</span>
<span class="bp">self</span><span class="o">.</span><span class="n">save</span><span class="p">(</span><span class="n">update_fields</span><span class="o">=</span><span class="p">[</span><span class="s2">&quot;db_location&quot;</span><span class="p">])</span>
<span class="n">location</span> <span class="o">=</span> <span class="nb">property</span><span class="p">(</span><span class="n">__location_get</span><span class="p">,</span> <span class="n">__location_set</span><span class="p">,</span> <span class="n">__location_del</span><span class="p">)</span>
<div class="viewcode-block" id="ObjectDB.at_db_location_postsave"><a class="viewcode-back" href="../../../api/evennia.objects.models.html#evennia.objects.models.ObjectDB.at_db_location_postsave">[docs]</a> <span class="k">def</span> <span class="nf">at_db_location_postsave</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">new</span><span class="p">):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;</span>
<span class="sd"> This is called automatically after the location field was</span>
<span class="sd"> saved, no matter how. It checks for a variable</span>
<span class="sd"> _safe_contents_update to know if the save was triggered via</span>
<span class="sd"> the location handler (which updates the contents cache) or</span>
<span class="sd"> not.</span>
<span class="sd"> Args:</span>
<span class="sd"> new (bool): Set if this location has not yet been saved before.</span>
<span class="sd"> &quot;&quot;&quot;</span>
<span class="k">if</span> <span class="ow">not</span> <span class="nb">hasattr</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="s2">&quot;_safe_contents_update&quot;</span><span class="p">):</span>
<span class="c1"># changed/set outside of the location handler</span>
<span class="k">if</span> <span class="n">new</span><span class="p">:</span>
<span class="c1"># if new, there is no previous location to worry about</span>
<span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">db_location</span><span class="p">:</span>
<span class="bp">self</span><span class="o">.</span><span class="n">db_location</span><span class="o">.</span><span class="n">contents_cache</span><span class="o">.</span><span class="n">add</span><span class="p">(</span><span class="bp">self</span><span class="p">)</span>
<span class="k">else</span><span class="p">:</span>
<span class="c1"># Since we cannot know at this point was old_location was, we</span>
<span class="c1"># trigger a full-on contents_cache update here.</span>
<span class="n">logger</span><span class="o">.</span><span class="n">log_warn</span><span class="p">(</span>
<span class="s2">&quot;db_location direct save triggered contents_cache.init() for all objects!&quot;</span>
<span class="p">)</span>
<span class="p">[</span><span class="n">o</span><span class="o">.</span><span class="n">contents_cache</span><span class="o">.</span><span class="n">init</span><span class="p">()</span> <span class="k">for</span> <span class="n">o</span> <span class="ow">in</span> <span class="bp">self</span><span class="o">.</span><span class="n">__dbclass__</span><span class="o">.</span><span class="n">get_all_cached_instances</span><span class="p">()]</span></div>
<span class="k">class</span> <span class="nc">Meta</span><span class="p">:</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;Define Django meta options&quot;&quot;&quot;</span>
<span class="n">verbose_name</span> <span class="o">=</span> <span class="s2">&quot;Object&quot;</span>
<span class="n">verbose_name_plural</span> <span class="o">=</span> <span class="s2">&quot;Objects&quot;</span></div>
</pre></div>
</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="nav-item nav-item-0"><a href="../../../index.html">Evennia latest</a> &#187;</li>
<li class="nav-item nav-item-1"><a href="../../index.html" >Module code</a> &#187;</li>
<li class="nav-item nav-item-2"><a href="../../evennia.html" >evennia</a> &#187;</li>
<li class="nav-item nav-item-this"><a href="">evennia.objects.models</a></li>
</ul>
</div>
<div class="footer" role="contentinfo">
&#169; Copyright 2024, The Evennia developer community.
Created using <a href="https://www.sphinx-doc.org/">Sphinx</a> 3.2.1.
</div>
</body>
</html>