mirror of
https://github.com/evennia/evennia.git
synced 2026-03-21 23:36:30 +01:00
404 lines
No EOL
46 KiB
HTML
404 lines
No EOL
46 KiB
HTML
<!DOCTYPE html>
|
||
|
||
<html lang="en" data-content_root="../">
|
||
<head>
|
||
<meta charset="utf-8" />
|
||
<meta name="viewport" content="width=device-width, initial-scale=1.0" /><meta name="viewport" content="width=device-width, initial-scale=1" />
|
||
|
||
<title>Typeclasses — Evennia latest documentation</title>
|
||
<link rel="stylesheet" type="text/css" href="../_static/pygments.css?v=d75fae25" />
|
||
<link rel="stylesheet" type="text/css" href="../_static/nature.css?v=279e0f84" />
|
||
<link rel="stylesheet" type="text/css" href="../_static/custom.css?v=e4a91a55" />
|
||
<script src="../_static/documentation_options.js?v=c6e86fd7"></script>
|
||
<script src="../_static/doctools.js?v=9bcbadda"></script>
|
||
<script src="../_static/sphinx_highlight.js?v=dc90522c"></script>
|
||
<link rel="icon" href="../_static/favicon.ico"/>
|
||
<link rel="index" title="Index" href="../genindex.html" />
|
||
<link rel="search" title="Search" href="../search.html" />
|
||
<link rel="next" title="Accounts" href="Accounts.html" />
|
||
<link rel="prev" title="Sessions" href="Sessions.html" />
|
||
</head><body>
|
||
<div class="related" role="navigation" aria-label="Related">
|
||
<h3>Navigation</h3>
|
||
<ul>
|
||
<li class="right" style="margin-right: 10px">
|
||
<a href="../genindex.html" title="General Index"
|
||
accesskey="I">index</a></li>
|
||
<li class="right" >
|
||
<a href="../py-modindex.html" title="Python Module Index"
|
||
>modules</a> |</li>
|
||
<li class="right" >
|
||
<a href="Accounts.html" title="Accounts"
|
||
accesskey="N">next</a> |</li>
|
||
<li class="right" >
|
||
<a href="Sessions.html" title="Sessions"
|
||
accesskey="P">previous</a> |</li>
|
||
<li class="nav-item nav-item-0"><a href="../index.html">Evennia</a> »</li>
|
||
<li class="nav-item nav-item-1"><a href="Components-Overview.html" accesskey="U">Core Components</a> »</li>
|
||
<li class="nav-item nav-item-this"><a href="">Typeclasses</a></li>
|
||
</ul>
|
||
</div>
|
||
|
||
<div class="document">
|
||
<div class="documentwrapper">
|
||
<div class="bodywrapper">
|
||
<div class="body" role="main">
|
||
|
||
<section class="tex2jax_ignore mathjax_ignore" id="typeclasses">
|
||
<h1>Typeclasses<a class="headerlink" href="#typeclasses" title="Link to this heading">¶</a></h1>
|
||
<p><em>Typeclasses</em> form the core of Evennia’s data storage. It allows Evennia to represent any number of different game entities as Python classes, without having to modify the database schema for every new type.</p>
|
||
<p>In Evennia the most important game entities, <a class="reference internal" href="Accounts.html"><span class="std std-doc">Accounts</span></a>, <a class="reference internal" href="Objects.html"><span class="std std-doc">Objects</span></a>, <a class="reference internal" href="Scripts.html"><span class="std std-doc">Scripts</span></a> and <a class="reference internal" href="Channels.html"><span class="std std-doc">Channels</span></a> are all Python classes inheriting, at varying distance, from <code class="docutils literal notranslate"><span class="pre">evennia.typeclasses.models.TypedObject</span></code>. In the documentation we refer to these objects as being “typeclassed” or even “being a typeclass”.</p>
|
||
<p>This is how the inheritance looks for the typeclasses in Evennia:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span> ┌───────────┐
|
||
│TypedObject│
|
||
└─────▲─────┘
|
||
┌───────────────┬────────┴──────┬────────────────┐
|
||
┌────┴────┐ ┌────┴───┐ ┌────┴────┐ ┌────┴───┐
|
||
1: │AccountDB│ │ScriptDB│ │ChannelDB│ │ObjectDB│
|
||
└────▲────┘ └────▲───┘ └────▲────┘ └────▲───┘
|
||
┌───────┴──────┐ ┌──────┴──────┐ ┌──────┴───────┐ ┌──────┴──────┐
|
||
2: │DefaultAccount│ │DefaultScript│ │DefaultChannel│ │DefaultObject│
|
||
└───────▲──────┘ └──────▲──────┘ └──────▲───────┘ └──────▲──────┘
|
||
│ │ │ │ Evennia
|
||
────────┼───────────────┼───────────────┼────────────────┼─────────
|
||
│ │ │ │ Gamedir
|
||
┌───┴───┐ ┌───┴──┐ ┌───┴───┐ ┌──────┐ │
|
||
3: │Account│ │Script│ │Channel│ │Object├─┤
|
||
└───────┘ └──────┘ └───────┘ └──────┘ │
|
||
┌─────────┐ │
|
||
│Character├─┤
|
||
└─────────┘ │
|
||
┌────┐ │
|
||
│Room├─┤
|
||
└────┘ │
|
||
┌────┐ │
|
||
│Exit├─┘
|
||
└────┘
|
||
</pre></div>
|
||
</div>
|
||
<ul class="simple">
|
||
<li><p><strong>Level 1</strong> above is the “database model” level. This describes the database tables and fields (this is technically a <a class="reference external" href="https://docs.djangoproject.com/en/4.1/topics/db/models/">Django model</a>).</p></li>
|
||
<li><p><strong>Level 2</strong> is where we find Evennia’s default implementations of the various game entities, on top of the database. These classes define all the hook methods that Evennia calls in various situations. <code class="docutils literal notranslate"><span class="pre">DefaultObject</span></code> is a little special since it’s the parent for <code class="docutils literal notranslate"><span class="pre">DefaultCharacter</span></code>, <code class="docutils literal notranslate"><span class="pre">DefaultRoom</span></code> and <code class="docutils literal notranslate"><span class="pre">DefaultExit</span></code>. They are all grouped under level 2 because they all represents defaults to build from.</p></li>
|
||
<li><p><strong>Level 3</strong>, finally, holds empty template classes created in your game directory. This is the level you are meant to modify and tweak as you please, overloading the defaults as befits your game. The templates inherit directly from their defaults, so <code class="docutils literal notranslate"><span class="pre">Object</span></code> inherits from <code class="docutils literal notranslate"><span class="pre">DefaultObject</span></code> and <code class="docutils literal notranslate"><span class="pre">Room</span></code> inherits from <code class="docutils literal notranslate"><span class="pre">DefaultRoom</span></code>.</p></li>
|
||
</ul>
|
||
<blockquote>
|
||
<div><p>This diagram doesn’t include the <code class="docutils literal notranslate"><span class="pre">ObjectParent</span></code> mixin for <code class="docutils literal notranslate"><span class="pre">Object</span></code>, <code class="docutils literal notranslate"><span class="pre">Character</span></code>, <code class="docutils literal notranslate"><span class="pre">Room</span></code> and <code class="docutils literal notranslate"><span class="pre">Exit</span></code>. This establishes a common parent for those classes, for shared properties. See <a class="reference internal" href="Objects.html"><span class="std std-doc">Objects</span></a> for more details.</p>
|
||
</div></blockquote>
|
||
<p>The <code class="docutils literal notranslate"><span class="pre">typeclass/list</span></code> command will provide a list of all typeclasses known to Evennia. This can be useful for getting a feel for what is available. Note however that if you add a new module with a class in it but do not import that module from anywhere, the <code class="docutils literal notranslate"><span class="pre">typeclass/list</span></code> will not find it. To make it known to Evennia you must import that module from somewhere.</p>
|
||
<section id="difference-between-typeclasses-and-classes">
|
||
<h2>Difference between typeclasses and classes<a class="headerlink" href="#difference-between-typeclasses-and-classes" title="Link to this heading">¶</a></h2>
|
||
<p>All Evennia classes inheriting from class in the table above share one important feature and two important limitations. This is why we don’t simply call them “classes” but “typeclasses”.</p>
|
||
<ol class="arabic">
|
||
<li><p>A typeclass can save itself to the database. This means that some properties (actually not that many) on the class actually represents database fields and can only hold very specific data types.</p></li>
|
||
<li><p>Due to its connection to the database, the typeclass’ name must be <em>unique</em> across the <em>entire</em> server namespace. That is, there must never be two same-named classes defined anywhere. So the below code would give an error (since <code class="docutils literal notranslate"><span class="pre">DefaultObject</span></code> is now globally found both in this module and in the default library):</p>
|
||
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="kn">from</span><span class="w"> </span><span class="nn">evennia</span><span class="w"> </span><span class="kn">import</span> <span class="n">DefaultObject</span> <span class="k">as</span> <span class="n">BaseObject</span>
|
||
<span class="k">class</span><span class="w"> </span><span class="nc">DefaultObject</span><span class="p">(</span><span class="n">BaseObject</span><span class="p">):</span>
|
||
<span class="k">pass</span>
|
||
</pre></div>
|
||
</div>
|
||
</li>
|
||
<li><p>A typeclass’ <code class="docutils literal notranslate"><span class="pre">__init__</span></code> method should normally not be overloaded. This has mostly to do with the fact that the <code class="docutils literal notranslate"><span class="pre">__init__</span></code> method is not called in a predictable way. Instead Evennia suggest you use the <code class="docutils literal notranslate"><span class="pre">at_*_creation</span></code> hooks (like <code class="docutils literal notranslate"><span class="pre">at_object_creation</span></code> for Objects) for setting things the very first time the typeclass is saved to the database or the <code class="docutils literal notranslate"><span class="pre">at_init</span></code> hook which is called every time the object is cached to memory. If you know what you are doing and want to use <code class="docutils literal notranslate"><span class="pre">__init__</span></code>, it <em>must</em> both accept arbitrary keyword arguments and use <code class="docutils literal notranslate"><span class="pre">super</span></code> to call its parent:</p>
|
||
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="k">def</span><span class="w"> </span><span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">):</span>
|
||
<span class="c1"># my content</span>
|
||
<span class="nb">super</span><span class="p">()</span><span class="o">.</span><span class="fm">__init__</span><span class="p">(</span><span class="o">**</span><span class="n">kwargs</span><span class="p">)</span>
|
||
<span class="c1"># my content</span>
|
||
</pre></div>
|
||
</div>
|
||
</li>
|
||
</ol>
|
||
<p>Apart from this, a typeclass works like any normal Python class and you can treat it as such.</p>
|
||
</section>
|
||
<section id="working-with-typeclasses">
|
||
<h2>Working with typeclasses<a class="headerlink" href="#working-with-typeclasses" title="Link to this heading">¶</a></h2>
|
||
<section id="creating-a-new-typeclass">
|
||
<h3>Creating a new typeclass<a class="headerlink" href="#creating-a-new-typeclass" title="Link to this heading">¶</a></h3>
|
||
<p>It’s easy to work with Typeclasses. Either you use an existing typeclass or you create a new Python class inheriting from an existing typeclass. Here is an example of creating a new type of Object:</p>
|
||
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span> <span class="kn">from</span><span class="w"> </span><span class="nn">evennia</span><span class="w"> </span><span class="kn">import</span> <span class="n">DefaultObject</span>
|
||
|
||
<span class="k">class</span><span class="w"> </span><span class="nc">Furniture</span><span class="p">(</span><span class="n">DefaultObject</span><span class="p">):</span>
|
||
<span class="c1"># this defines what 'furniture' is, like</span>
|
||
<span class="c1"># storing who sits on it or something.</span>
|
||
<span class="k">pass</span>
|
||
|
||
</pre></div>
|
||
</div>
|
||
<p>You can now create a new <code class="docutils literal notranslate"><span class="pre">Furniture</span></code> object in two ways. First (and usually not the most
|
||
convenient) way is to create an instance of the class and then save it manually to the database:</p>
|
||
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="n">chair</span> <span class="o">=</span> <span class="n">Furniture</span><span class="p">(</span><span class="n">db_key</span><span class="o">=</span><span class="s2">"Chair"</span><span class="p">)</span>
|
||
<span class="n">chair</span><span class="o">.</span><span class="n">save</span><span class="p">()</span>
|
||
|
||
</pre></div>
|
||
</div>
|
||
<p>To use this you must give the database field names as keywords to the call. Which are available depends on the entity you are creating, but all start with <code class="docutils literal notranslate"><span class="pre">db_*</span></code> in Evennia. This is a method you may be familiar with if you know Django from before.</p>
|
||
<p>It is recommended that you instead use the <code class="docutils literal notranslate"><span class="pre">create_*</span></code> functions to create typeclassed entities:</p>
|
||
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="kn">from</span><span class="w"> </span><span class="nn">evennia</span><span class="w"> </span><span class="kn">import</span> <span class="n">create_object</span>
|
||
|
||
<span class="n">chair</span> <span class="o">=</span> <span class="n">create_object</span><span class="p">(</span><span class="n">Furniture</span><span class="p">,</span> <span class="n">key</span><span class="o">=</span><span class="s2">"Chair"</span><span class="p">)</span>
|
||
<span class="c1"># or (if your typeclass is in a module furniture.py)</span>
|
||
<span class="n">chair</span> <span class="o">=</span> <span class="n">create_object</span><span class="p">(</span><span class="s2">"furniture.Furniture"</span><span class="p">,</span> <span class="n">key</span><span class="o">=</span><span class="s2">"Chair"</span><span class="p">)</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>The <code class="docutils literal notranslate"><span class="pre">create_object</span></code> (<code class="docutils literal notranslate"><span class="pre">create_account</span></code>, <code class="docutils literal notranslate"><span class="pre">create_script</span></code> etc) takes the typeclass as its first argument; this can both be the actual class or the python path to the typeclass as found under your game directory. So if your <code class="docutils literal notranslate"><span class="pre">Furniture</span></code> typeclass sits in <code class="docutils literal notranslate"><span class="pre">mygame/typeclasses/furniture.py</span></code>, you could point to it as <code class="docutils literal notranslate"><span class="pre">typeclasses.furniture.Furniture</span></code>. Since Evennia will itself look in <code class="docutils literal notranslate"><span class="pre">mygame/typeclasses</span></code>, you can shorten this even further to just <code class="docutils literal notranslate"><span class="pre">furniture.Furniture</span></code>. The create-functions take a lot of extra keywords allowing you to set things like <a class="reference internal" href="Attributes.html"><span class="std std-doc">Attributes</span></a> and <a class="reference internal" href="Tags.html"><span class="std std-doc">Tags</span></a> all in one go. These keywords don’t use the <code class="docutils literal notranslate"><span class="pre">db_*</span></code> prefix. This will also automatically save the new instance to the database, so you don’t need to call <code class="docutils literal notranslate"><span class="pre">save()</span></code> explicitly.</p>
|
||
<p>An example of a database field is <code class="docutils literal notranslate"><span class="pre">db_key</span></code>. This stores the “name” of the entity you are modifying and can thus only hold a string. This is one way of making sure to update the <code class="docutils literal notranslate"><span class="pre">db_key</span></code>:</p>
|
||
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="n">chair</span><span class="o">.</span><span class="n">db_key</span> <span class="o">=</span> <span class="s2">"Table"</span>
|
||
<span class="n">chair</span><span class="o">.</span><span class="n">save</span><span class="p">()</span>
|
||
|
||
<span class="nb">print</span><span class="p">(</span><span class="n">chair</span><span class="o">.</span><span class="n">db_key</span><span class="p">)</span>
|
||
<span class="o"><<<</span> <span class="n">Table</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>That is, we change the chair object to have the <code class="docutils literal notranslate"><span class="pre">db_key</span></code> “Table”, then save this to the database. However, you almost never do things this way; Evennia defines property wrappers for all the database fields. These are named the same as the field, but without the <code class="docutils literal notranslate"><span class="pre">db_</span></code> part:</p>
|
||
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="n">chair</span><span class="o">.</span><span class="n">key</span> <span class="o">=</span> <span class="s2">"Table"</span>
|
||
|
||
<span class="nb">print</span><span class="p">(</span><span class="n">chair</span><span class="o">.</span><span class="n">key</span><span class="p">)</span>
|
||
<span class="o"><<<</span> <span class="n">Table</span>
|
||
|
||
</pre></div>
|
||
</div>
|
||
<p>The <code class="docutils literal notranslate"><span class="pre">key</span></code> wrapper is not only shorter to write, it will make sure to save the field for you, and does so more efficiently by levering sql update mechanics under the hood. So whereas it is good to be aware that the field is named <code class="docutils literal notranslate"><span class="pre">db_key</span></code> you should use <code class="docutils literal notranslate"><span class="pre">key</span></code> as much as you can.</p>
|
||
<p>Each typeclass entity has some unique fields relevant to that type. But all also share the
|
||
following fields (the wrapper name without <code class="docutils literal notranslate"><span class="pre">db_</span></code> is given):</p>
|
||
<ul class="simple">
|
||
<li><p><code class="docutils literal notranslate"><span class="pre">key</span></code> (str): The main identifier for the entity, like “Rose”, “myscript” or “Paul”. <code class="docutils literal notranslate"><span class="pre">name</span></code> is an alias.</p></li>
|
||
<li><p><code class="docutils literal notranslate"><span class="pre">date_created</span></code> (datetime): Time stamp when this object was created.</p></li>
|
||
<li><p><code class="docutils literal notranslate"><span class="pre">typeclass_path</span></code> (str): A python path pointing to the location of this (type)class</p></li>
|
||
</ul>
|
||
<p>There is one special field that doesn’t use the <code class="docutils literal notranslate"><span class="pre">db_</span></code> prefix (it’s defined by Django):</p>
|
||
<ul class="simple">
|
||
<li><p><code class="docutils literal notranslate"><span class="pre">id</span></code> (int): the database id (database ref) of the object. This is an ever-increasing, unique integer. It can also be accessed as <code class="docutils literal notranslate"><span class="pre">dbid</span></code> (database ID) or <code class="docutils literal notranslate"><span class="pre">pk</span></code> (primary key). The <code class="docutils literal notranslate"><span class="pre">dbref</span></code> property returns the string form “#id”.</p></li>
|
||
</ul>
|
||
<p>The typeclassed entity has several common handlers:</p>
|
||
<ul class="simple">
|
||
<li><p><code class="docutils literal notranslate"><span class="pre">tags</span></code> - the <a class="reference internal" href="Tags.html"><span class="std std-doc">TagHandler</span></a> that handles tagging. Use <code class="docutils literal notranslate"><span class="pre">tags.add()</span></code> , <code class="docutils literal notranslate"><span class="pre">tags.get()</span></code> etc.</p></li>
|
||
<li><p><code class="docutils literal notranslate"><span class="pre">locks</span></code> - the <a class="reference internal" href="Locks.html"><span class="std std-doc">LockHandler</span></a> that manages access restrictions. Use <code class="docutils literal notranslate"><span class="pre">locks.add()</span></code>, <code class="docutils literal notranslate"><span class="pre">locks.get()</span></code> etc.</p></li>
|
||
<li><p><code class="docutils literal notranslate"><span class="pre">attributes</span></code> - the <a class="reference internal" href="Attributes.html"><span class="std std-doc">AttributeHandler</span></a> that manages Attributes on the object. Use <code class="docutils literal notranslate"><span class="pre">attributes.add()</span></code>
|
||
etc.</p></li>
|
||
<li><p><code class="docutils literal notranslate"><span class="pre">db</span></code> (DataBase) - a shortcut property to the AttributeHandler; allowing <code class="docutils literal notranslate"><span class="pre">obj.db.attrname</span> <span class="pre">=</span> <span class="pre">value</span></code></p></li>
|
||
<li><p><code class="docutils literal notranslate"><span class="pre">nattributes</span></code> - the <a class="reference internal" href="Attributes.html"><span class="std std-doc">Non-persistent AttributeHandler</span></a> for attributes not saved in the
|
||
database.</p></li>
|
||
<li><p><code class="docutils literal notranslate"><span class="pre">ndb</span></code> (NotDataBase) - a shortcut property to the Non-peristent AttributeHandler. Allows <code class="docutils literal notranslate"><span class="pre">obj.ndb.attrname</span> <span class="pre">=</span> <span class="pre">value</span></code></p></li>
|
||
</ul>
|
||
<p>Each of the typeclassed entities then extend this list with their own properties. Go to the respective pages for <a class="reference internal" href="Objects.html"><span class="std std-doc">Objects</span></a>, <a class="reference internal" href="Scripts.html"><span class="std std-doc">Scripts</span></a>, <a class="reference internal" href="Accounts.html"><span class="std std-doc">Accounts</span></a> and <a class="reference internal" href="Channels.html"><span class="std std-doc">Channels</span></a> for more info. It’s also recommended that you explore the available entities using <a class="reference internal" href="../Evennia-API.html"><span class="std std-doc">Evennia’s flat API</span></a> to explore which properties and methods they have available.</p>
|
||
</section>
|
||
<section id="overloading-hooks">
|
||
<h3>Overloading hooks<a class="headerlink" href="#overloading-hooks" title="Link to this heading">¶</a></h3>
|
||
<p>The way to customize typeclasses is usually to overload <em>hook methods</em> on them. Hooks are methods that Evennia call in various situations. An example is the <code class="docutils literal notranslate"><span class="pre">at_object_creation</span></code> hook on <code class="docutils literal notranslate"><span class="pre">Objects</span></code>, which is only called once, the very first time this object is saved to the database. Other examples are the <code class="docutils literal notranslate"><span class="pre">at_login</span></code> hook of Accounts and the <code class="docutils literal notranslate"><span class="pre">at_repeat</span></code> hook of Scripts.</p>
|
||
</section>
|
||
<section id="querying-for-typeclasses">
|
||
<h3>Querying for typeclasses<a class="headerlink" href="#querying-for-typeclasses" title="Link to this heading">¶</a></h3>
|
||
<p>Most of the time you search for objects in the database by using convenience methods like the <code class="docutils literal notranslate"><span class="pre">caller.search()</span></code> of <a class="reference internal" href="Commands.html"><span class="std std-doc">Commands</span></a> or the search functions like <code class="docutils literal notranslate"><span class="pre">evennia.search_objects</span></code>.</p>
|
||
<p>You can however also query for them directly using <a class="reference external" href="https://docs.djangoproject.com/en/4.1/topics/db/queries/">Django’s query language</a>. This makes use of a <em>database manager</em> that sits on all typeclasses, named <code class="docutils literal notranslate"><span class="pre">objects</span></code>. This manager holds methods that allow database searches against that particular type of object (this is the way Django normally works too). When using Django queries, you need to use the full field names (like <code class="docutils literal notranslate"><span class="pre">db_key</span></code>) to search:</p>
|
||
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="n">matches</span> <span class="o">=</span> <span class="n">Furniture</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="n">db_key</span><span class="o">=</span><span class="s2">"Chair"</span><span class="p">)</span>
|
||
|
||
</pre></div>
|
||
</div>
|
||
<p>It is important that this will <em>only</em> find objects inheriting directly from <code class="docutils literal notranslate"><span class="pre">Furniture</span></code> in your database. If there was a subclass of <code class="docutils literal notranslate"><span class="pre">Furniture</span></code> named <code class="docutils literal notranslate"><span class="pre">Sitables</span></code> you would not find any chairs derived from <code class="docutils literal notranslate"><span class="pre">Sitables</span></code> with this query (this is not a Django feature but special to Evennia). To find objects from subclasses Evennia instead makes the <code class="docutils literal notranslate"><span class="pre">get_family</span></code> and <code class="docutils literal notranslate"><span class="pre">filter_family</span></code> query methods available:</p>
|
||
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="c1"># search for all furnitures and subclasses of furnitures</span>
|
||
<span class="c1"># whose names starts with "Chair"</span>
|
||
<span class="n">matches</span> <span class="o">=</span> <span class="n">Furniture</span><span class="o">.</span><span class="n">objects</span><span class="o">.</span><span class="n">filter_family</span><span class="p">(</span><span class="n">db_key__startswith</span><span class="o">=</span><span class="s2">"Chair"</span><span class="p">)</span>
|
||
|
||
</pre></div>
|
||
</div>
|
||
<p>To make sure to search, say, all <code class="docutils literal notranslate"><span class="pre">Scripts</span></code> <em>regardless</em> of typeclass, you need to query from the database model itself. So for Objects, this would be <code class="docutils literal notranslate"><span class="pre">ObjectDB</span></code> in the diagram above. Here’s an example for Scripts:</p>
|
||
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="kn">from</span><span class="w"> </span><span class="nn">evennia</span><span class="w"> </span><span class="kn">import</span> <span class="n">ScriptDB</span>
|
||
<span class="n">matches</span> <span class="o">=</span> <span class="n">ScriptDB</span><span class="o">.</span><span class="n">objects</span><span class="o">.</span><span class="n">filter</span><span class="p">(</span><span class="n">db_key__contains</span><span class="o">=</span><span class="s2">"Combat"</span><span class="p">)</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>When querying from the database model parent you don’t need to use <code class="docutils literal notranslate"><span class="pre">filter_family</span></code> or <code class="docutils literal notranslate"><span class="pre">get_family</span></code> - you will always query all children on the database model.</p>
|
||
</section>
|
||
<section id="updating-existing-typeclass-instances">
|
||
<h3>Updating existing typeclass instances<a class="headerlink" href="#updating-existing-typeclass-instances" title="Link to this heading">¶</a></h3>
|
||
<p>If you already have created instances of Typeclasses, you can modify the <em>Python code</em> at any time - due to how Python inheritance works your changes will automatically be applied to all children once you have reloaded the server. However, database-saved data, like <code class="docutils literal notranslate"><span class="pre">db_*</span></code> fields, <a class="reference internal" href="Attributes.html"><span class="std std-doc">Attributes</span></a>, <a class="reference internal" href="Tags.html"><span class="std std-doc">Tags</span></a> etc, are not themselves embedded into the class and will <em>not</em> be updated automatically. This you need to manage yourself, by searching for all relevant objects and updating or adding the data:</p>
|
||
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="c1"># add a worth Attribute to all existing Furniture</span>
|
||
<span class="k">for</span> <span class="n">obj</span> <span class="ow">in</span> <span class="n">Furniture</span><span class="o">.</span><span class="n">objects</span><span class="o">.</span><span class="n">all</span><span class="p">():</span>
|
||
<span class="c1"># this will loop over all Furniture instances</span>
|
||
<span class="n">obj</span><span class="o">.</span><span class="n">db</span><span class="o">.</span><span class="n">worth</span> <span class="o">=</span> <span class="mi">100</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>A common use case is putting all Attributes in the <code class="docutils literal notranslate"><span class="pre">at_*_creation</span></code> hook of the entity, such as <code class="docutils literal notranslate"><span class="pre">at_object_creation</span></code> for <code class="docutils literal notranslate"><span class="pre">Objects</span></code>. This is called every time an object is created - and only then. This is usually what you want but it does mean already existing objects won’t get updated if you change the contents of <code class="docutils literal notranslate"><span class="pre">at_object_creation</span></code> later. You can fix this in a similar way as above (manually setting each Attribute) or with something like this:</p>
|
||
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="c1"># Re-run at_object_creation only on those objects not having the new Attribute</span>
|
||
<span class="k">for</span> <span class="n">obj</span> <span class="ow">in</span> <span class="n">Furniture</span><span class="o">.</span><span class="n">objects</span><span class="o">.</span><span class="n">all</span><span class="p">():</span>
|
||
<span class="k">if</span> <span class="ow">not</span> <span class="n">obj</span><span class="o">.</span><span class="n">db</span><span class="o">.</span><span class="n">worth</span><span class="p">:</span>
|
||
<span class="n">obj</span><span class="o">.</span><span class="n">at_object_creation</span><span class="p">()</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>The above examples can be run in the command prompt created by <code class="docutils literal notranslate"><span class="pre">evennia</span> <span class="pre">shell</span></code>. You could also run it all in-game using <code class="docutils literal notranslate"><span class="pre">@py</span></code>. That however requires you to put the code (including imports) as one single line using <code class="docutils literal notranslate"><span class="pre">;</span></code> and <a class="reference external" href="http://www.secnetix.de/olli/Python/list_comprehensions.hawk">list comprehensions</a>, like this (ignore the line break, that’s only for readability in the wiki):</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">py</span> <span class="kn">from</span><span class="w"> </span><span class="nn">typeclasses.furniture</span><span class="w"> </span><span class="kn">import</span> <span class="n">Furniture</span><span class="p">;</span>
|
||
<span class="p">[</span><span class="n">obj</span><span class="o">.</span><span class="n">at_object_creation</span><span class="p">()</span> <span class="k">for</span> <span class="n">obj</span> <span class="ow">in</span> <span class="n">Furniture</span><span class="o">.</span><span class="n">objects</span><span class="o">.</span><span class="n">all</span><span class="p">()</span> <span class="k">if</span> <span class="ow">not</span> <span class="n">obj</span><span class="o">.</span><span class="n">db</span><span class="o">.</span><span class="n">worth</span><span class="p">]</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>It is recommended that you plan your game properly before starting to build, to avoid having to retroactively update objects more than necessary.</p>
|
||
</section>
|
||
<section id="swap-typeclass">
|
||
<h3>Swap typeclass<a class="headerlink" href="#swap-typeclass" title="Link to this heading">¶</a></h3>
|
||
<p>If you want to swap an already existing typeclass, there are two ways to do so: From in-game and via code. From inside the game you can use the default <code class="docutils literal notranslate"><span class="pre">@typeclass</span></code> command:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">typeclass</span> <span class="n">objname</span> <span class="o">=</span> <span class="n">path</span><span class="o">.</span><span class="n">to</span><span class="o">.</span><span class="n">new</span><span class="o">.</span><span class="n">typeclass</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>There are two important switches to this command:</p>
|
||
<ul class="simple">
|
||
<li><p><code class="docutils literal notranslate"><span class="pre">/reset</span></code> - This will purge all existing Attributes on the object and re-run the creation hook (like <code class="docutils literal notranslate"><span class="pre">at_object_creation</span></code> for Objects). This assures you get an object which is purely of this new class.</p></li>
|
||
<li><p><code class="docutils literal notranslate"><span class="pre">/force</span></code> - This is required if you are changing the class to be <em>the same</em> class the object already has - it’s a safety check to avoid user errors. This is usually used together with <code class="docutils literal notranslate"><span class="pre">/reset</span></code> to re-run the creation hook on an existing class.</p></li>
|
||
</ul>
|
||
<p>In code you instead use the <code class="docutils literal notranslate"><span class="pre">swap_typeclass</span></code> method which you can find on all typeclassed entities:</p>
|
||
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="n">obj_to_change</span><span class="o">.</span><span class="n">swap_typeclass</span><span class="p">(</span><span class="n">new_typeclass_path</span><span class="p">,</span> <span class="n">clean_attributes</span><span class="o">=</span><span class="kc">False</span><span class="p">,</span>
|
||
<span class="n">run_start_hooks</span><span class="o">=</span><span class="s2">"all"</span><span class="p">,</span> <span class="n">no_default</span><span class="o">=</span><span class="kc">True</span><span class="p">,</span> <span class="n">clean_cmdsets</span><span class="o">=</span><span class="kc">False</span><span class="p">)</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>The arguments to this method are described <a class="reference external" href="https://github.com/evennia/evennia/blob/main/evennia.typeclasses.models#typedobjectswap_typeclass">in the API docs here</a>.</p>
|
||
</section>
|
||
</section>
|
||
<section id="how-typeclasses-actually-work">
|
||
<h2>How typeclasses actually work<a class="headerlink" href="#how-typeclasses-actually-work" title="Link to this heading">¶</a></h2>
|
||
<p><em>This is considered an advanced section.</em></p>
|
||
<p>Technically, typeclasses are <a class="reference external" href="https://docs.djangoproject.com/en/4.1/topics/db/models/#proxy-models">Django proxy models</a>. The only database models that are “real” in the typeclass system (that is, are represented by actual tables in the database) are <code class="docutils literal notranslate"><span class="pre">AccountDB</span></code>, <code class="docutils literal notranslate"><span class="pre">ObjectDB</span></code>, <code class="docutils literal notranslate"><span class="pre">ScriptDB</span></code> and <code class="docutils literal notranslate"><span class="pre">ChannelDB</span></code> (there are also <a class="reference internal" href="Attributes.html"><span class="std std-doc">Attributes</span></a> and <a class="reference internal" href="Tags.html"><span class="std std-doc">Tags</span></a> but they are not typeclasses themselves). All the subclasses of them are “proxies”, extending them with Python code without actually modifying the database layout.</p>
|
||
<p>Evennia modifies Django’s proxy model in various ways to allow them to work without any boiler plate (for example you don’t need to set the Django “proxy” property in the model <code class="docutils literal notranslate"><span class="pre">Meta</span></code> subclass, Evennia handles this for you using metaclasses). Evennia also makes sure you can query subclasses as well as patches Django to allow multiple inheritance from the same base class.</p>
|
||
<section id="caveats">
|
||
<h3>Caveats<a class="headerlink" href="#caveats" title="Link to this heading">¶</a></h3>
|
||
<p>Evennia uses the <em>idmapper</em> to cache its typeclasses (Django proxy models) in memory. The idmapper allows things like on-object handlers and properties to be stored on typeclass instances and to not get lost as long as the server is running (they will only be cleared on a Server reload). Django does not work like this by default; by default every time you search for an object in the database you’ll get a <em>different</em> instance of that object back and anything you stored on it that was not in the database would be lost. The bottom line is that Evennia’s Typeclass instances subsist in memory a lot longer than vanilla Django model instances do.</p>
|
||
<p>There is one caveat to consider with this, and that relates to [making your own models](New-
|
||
Models): Foreign relationships to typeclasses are cached by Django and that means that if you were to change an object in a foreign relationship via some other means than via that relationship, the object seeing the relationship may not reliably update but will still see its old cached version. Due to typeclasses staying so long in memory, stale caches of such relationships could be more visible than common in Django. See the <a class="reference external" href="https://github.com/evennia/evennia/issues/1098">closed issue #1098 and its comments</a> for examples and solutions.</p>
|
||
</section>
|
||
</section>
|
||
<section id="will-i-run-out-of-dbrefs">
|
||
<h2>Will I run out of dbrefs?<a class="headerlink" href="#will-i-run-out-of-dbrefs" title="Link to this heading">¶</a></h2>
|
||
<p>Evennia does not re-use its <code class="docutils literal notranslate"><span class="pre">#dbrefs</span></code>. This means new objects get an ever-increasing <code class="docutils literal notranslate"><span class="pre">#dbref</span></code>, even if you delete older objects. There are technical and safety reasons for this. But you may wonder if this means you have to worry about a big game ‘running out’ of dbref integers eventually.</p>
|
||
<p>The answer is simply <strong>no</strong>.</p>
|
||
<p>For example, the max dbref value for the default sqlite3 database is <code class="docutils literal notranslate"><span class="pre">2**64</span></code>. If you <em>created 10 000 new objects every second of every minute of every day of the year it would take about <strong>60 million years</strong> for you to run out of dbref numbers</em>. That’s a database of 140 TeraBytes, just to store the dbrefs, no other data.</p>
|
||
<p>If you are still using Evennia at that point and have this concern, get back to us and we can discuss adding dbref reuse then.</p>
|
||
</section>
|
||
</section>
|
||
|
||
|
||
<div class="clearer"></div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="sphinxsidebar" role="navigation" aria-label="Main">
|
||
<div class="sphinxsidebarwrapper">
|
||
<p class="logo"><a href="../index.html">
|
||
<img class="logo" src="../_static/evennia_logo.png" alt="Logo of Evennia"/>
|
||
</a></p>
|
||
<search 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" autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false"/>
|
||
<input type="submit" value="Go" />
|
||
</form>
|
||
</div>
|
||
</search>
|
||
<script>document.getElementById('searchbox').style.display = "block"</script>
|
||
<h3><a href="../index.html">Table of Contents</a></h3>
|
||
<ul>
|
||
<li><a class="reference internal" href="#">Typeclasses</a><ul>
|
||
<li><a class="reference internal" href="#difference-between-typeclasses-and-classes">Difference between typeclasses and classes</a></li>
|
||
<li><a class="reference internal" href="#working-with-typeclasses">Working with typeclasses</a><ul>
|
||
<li><a class="reference internal" href="#creating-a-new-typeclass">Creating a new typeclass</a></li>
|
||
<li><a class="reference internal" href="#overloading-hooks">Overloading hooks</a></li>
|
||
<li><a class="reference internal" href="#querying-for-typeclasses">Querying for typeclasses</a></li>
|
||
<li><a class="reference internal" href="#updating-existing-typeclass-instances">Updating existing typeclass instances</a></li>
|
||
<li><a class="reference internal" href="#swap-typeclass">Swap typeclass</a></li>
|
||
</ul>
|
||
</li>
|
||
<li><a class="reference internal" href="#how-typeclasses-actually-work">How typeclasses actually work</a><ul>
|
||
<li><a class="reference internal" href="#caveats">Caveats</a></li>
|
||
</ul>
|
||
</li>
|
||
<li><a class="reference internal" href="#will-i-run-out-of-dbrefs">Will I run out of dbrefs?</a></li>
|
||
</ul>
|
||
</li>
|
||
</ul>
|
||
|
||
<div>
|
||
<h4>Previous topic</h4>
|
||
<p class="topless"><a href="Sessions.html"
|
||
title="previous chapter">Sessions</a></p>
|
||
</div>
|
||
<div>
|
||
<h4>Next topic</h4>
|
||
<p class="topless"><a href="Accounts.html"
|
||
title="next chapter">Accounts</a></p>
|
||
</div>
|
||
<div role="note" aria-label="source link">
|
||
<!--h3>This Page</h3-->
|
||
<ul class="this-page-menu">
|
||
<li><a href="../_sources/Components/Typeclasses.md.txt"
|
||
rel="nofollow">Show Page Source</a></li>
|
||
</ul>
|
||
</div><h3>Links</h3>
|
||
<ul>
|
||
<li><a href="https://www.evennia.com/docs/latest/index.html">Documentation Top</a> </li>
|
||
<li><a href="https://www.evennia.com">Evennia Home</a> </li>
|
||
<li><a href="https://github.com/evennia/evennia">Github</a> </li>
|
||
<li><a href="http://games.evennia.com">Game Index</a> </li>
|
||
<li>
|
||
<a href="https://discord.gg/AJJpcRUhtF">Discord</a> -
|
||
<a href="https://github.com/evennia/evennia/discussions">Discussions</a> -
|
||
<a href="https://evennia.blogspot.com/">Blog</a>
|
||
</li>
|
||
</ul>
|
||
<h3>Doc Versions</h3>
|
||
<ul>
|
||
|
||
<li>
|
||
<a href="https://www.evennia.com/docs/latest/index.html">latest (main branch)</a>
|
||
</li>
|
||
|
||
|
||
<li>
|
||
<a href="https://www.evennia.com/docs/5.x/index.html">v5.0.0 branch (outdated)</a>
|
||
</li>
|
||
|
||
<li>
|
||
<a href="https://www.evennia.com/docs/4.x/index.html">v4.0.0 branch (outdated)</a>
|
||
</li>
|
||
|
||
<li>
|
||
<a href="https://www.evennia.com/docs/3.x/index.html">v3.0.0 branch (outdated)</a>
|
||
</li>
|
||
|
||
<li>
|
||
<a href="https://www.evennia.com/docs/2.x/index.html">v2.0.0 branch (outdated)</a>
|
||
</li>
|
||
|
||
<li>
|
||
<a href="https://www.evennia.com/docs/1.x/index.html">v1.0.0 branch (outdated)</a>
|
||
</li>
|
||
|
||
<li>
|
||
<a href="https://www.evennia.com/docs/0.x/index.html">v0.9.5 branch (outdated)</a>
|
||
</li>
|
||
|
||
</ul>
|
||
|
||
</div>
|
||
</div>
|
||
<div class="clearer"></div>
|
||
</div>
|
||
<div class="related" role="navigation" aria-label="Related">
|
||
<h3>Navigation</h3>
|
||
<ul>
|
||
<li class="right" style="margin-right: 10px">
|
||
<a href="../genindex.html" title="General Index"
|
||
>index</a></li>
|
||
<li class="right" >
|
||
<a href="../py-modindex.html" title="Python Module Index"
|
||
>modules</a> |</li>
|
||
<li class="right" >
|
||
<a href="Accounts.html" title="Accounts"
|
||
>next</a> |</li>
|
||
<li class="right" >
|
||
<a href="Sessions.html" title="Sessions"
|
||
>previous</a> |</li>
|
||
<li class="nav-item nav-item-0"><a href="../index.html">Evennia</a> »</li>
|
||
<li class="nav-item nav-item-1"><a href="Components-Overview.html" >Core Components</a> »</li>
|
||
<li class="nav-item nav-item-this"><a href="">Typeclasses</a></li>
|
||
</ul>
|
||
</div>
|
||
<div class="footer" role="contentinfo">
|
||
© Copyright 2024, The Evennia developer community.
|
||
Created using <a href="https://www.sphinx-doc.org/">Sphinx</a> 8.2.3.
|
||
</div>
|
||
</body>
|
||
</html> |