<pclass="last">You are reading an old version of the Evennia documentation. <ahref="https://www.evennia.com/docs/latest/index.html">The latest version is here</a></p>.
<h1>Scripts<aclass="headerlink"href="#scripts"title="Permalink to this headline">¶</a></h1>
<p><aclass="reference internal"href="../api/evennia.scripts.scripts.html#evennia-scripts-scripts"><spanclass="std std-ref">Script API reference</span></a></p>
<p><em>Scripts</em> are the out-of-character siblings to the in-character <aclass="reference internal"href="Objects.html"><spanclass="doc std std-doc">Objects</span></a>. Scripts are so flexible that the name “Script” is a bit limiting in itself - but we had to pick <em>something</em> to name them. Other possible names (depending on what you’d use them for) would be <codeclass="docutils literal notranslate"><spanclass="pre">OOBObjects</span></code>, <codeclass="docutils literal notranslate"><spanclass="pre">StorageContainers</span></code> or <codeclass="docutils literal notranslate"><spanclass="pre">TimerObjects</span></code>.</p>
<p>If you ever consider creating an <aclass="reference internal"href="Objects.html"><spanclass="doc std std-doc">Object</span></a> with a <codeclass="docutils literal notranslate"><spanclass="pre">None</span></code>-location just to store some game data, you should really be using a Script instead.</p>
<ulclass="simple">
<li><p>Scripts are full <aclass="reference internal"href="Typeclasses.html"><spanclass="doc std std-doc">Typeclassed</span></a> entities - they have <aclass="reference internal"href="Attributes.html"><spanclass="doc std std-doc">Attributes</span></a> and can be modified in the same way. But they have <em>no in-game existence</em>, so no location or command-execution like <aclass="reference internal"href="Objects.html"><spanclass="doc std std-doc">Objects</span></a> and no connection to a particular player/session like <aclass="reference internal"href="Accounts.html"><spanclass="doc std std-doc">Accounts</span></a>. This means they are perfectly suitable for acting as database-storage backends for game <em>systems</em>: Storing the current state of the economy, who is involved in the current fight, tracking an ongoing barter and so on. They are great as persistent system handlers.</p></li>
<li><p>Scripts have an optional <em>timer component</em>. This means that you can set up the script to tick the <codeclass="docutils literal notranslate"><spanclass="pre">at_repeat</span></code> hook on the Script at a certain interval. The timer can be controlled independently of the rest of the script as needed. This component is optional and complementary to other timing functions in Evennia, like <aclass="reference internal"href="../api/evennia.utils.utils.html#evennia.utils.utils.delay"title="evennia.utils.utils.delay"><spanclass="xref myst py py-func">evennia.utils.delay</span></a> and <aclass="reference internal"href="../api/evennia.utils.utils.html#evennia.utils.utils.repeat"title="evennia.utils.utils.repeat"><spanclass="xref myst py py-func">evennia.utils.repeat</span></a>.</p></li>
<li><p>Scripts can <em>attach</em> to Objects and Accounts via e.g. <codeclass="docutils literal notranslate"><spanclass="pre">obj.scripts.add/remove</span></code>. In the script you can then access the object/account as <codeclass="docutils literal notranslate"><spanclass="pre">self.obj</span></code> or <codeclass="docutils literal notranslate"><spanclass="pre">self.account</span></code>. This can be used to dynamically extend other typeclasses but also to use the timer component to affect the parent object in various ways. For historical reasons, a Script <em>not</em> attached to an object is referred to as a <em>Global</em> Script.</p></li>
</ul>
<divclass="versionchanged">
<p><spanclass="versionmodified changed">Changed in version 1.0: </span>In previous Evennia versions, stopping the Script’s timer also meant deleting the Script object.
Starting with this version, the timer can be start/stopped separately and <codeclass="docutils literal notranslate"><spanclass="pre">.delete()</span></code> must be called
on the Script explicitly to delete it.</p>
</div>
<sectionid="working-with-scripts">
<h2>Working with Scripts<aclass="headerlink"href="#working-with-scripts"title="Permalink to this headline">¶</a></h2>
<p>There are two main commands controlling scripts in the default cmdset:</p>
<p>The <codeclass="docutils literal notranslate"><spanclass="pre">addscript</span></code> command is used for attaching scripts to existing objects:</p>
<p>The <codeclass="docutils literal notranslate"><spanclass="pre">scripts</span></code> command is used to view all scripts and perform operations on them:</p>
<p><spanclass="versionmodified changed">Changed in version 1.0: </span>The <codeclass="docutils literal notranslate"><spanclass="pre">addscript</span></code> command used to be only <codeclass="docutils literal notranslate"><spanclass="pre">script</span></code> which was easy to confuse with <codeclass="docutils literal notranslate"><spanclass="pre">scripts</span></code>.</p>
</div>
<sectionid="code-examples">
<h3>Code examples<aclass="headerlink"href="#code-examples"title="Permalink to this headline">¶</a></h3>
<p>Here are some examples of working with Scripts in-code (more details to follow in later
<divclass="highlight-python notranslate"><divclass="highlight"><pre><span></span><spanclass="c1"># (note that this will call `timed_script.at_repeat` which is empty by default)</span>
<spanclass="n">interval</span><spanclass="o">=</span><spanclass="mi">34</span><spanclass="p">,</span><spanclass="c1"># seconds <=0 means off</span>
<spanclass="n">start_delay</span><spanclass="o">=</span><spanclass="kc">True</span><spanclass="p">,</span><spanclass="c1"># wait interval before first call</span>
<divclass="highlight-python notranslate"><divclass="highlight"><pre><span></span><spanclass="c1"># regular search (this is always a list, also if there is only one match)</span>
<h3>Defining new Scripts<aclass="headerlink"href="#defining-new-scripts"title="Permalink to this headline">¶</a></h3>
<p>A Script is defined as a class and is created in the same way as other
<aclass="reference internal"href="Typeclasses.html"><spanclass="doc std std-doc">typeclassed</span></a> entities. The parent class is <codeclass="docutils literal notranslate"><spanclass="pre">evennia.DefaultScript</span></code>.</p>
<sectionid="simple-storage-script">
<h4>Simple storage script<aclass="headerlink"href="#simple-storage-script"title="Permalink to this headline">¶</a></h4>
<p>In <codeclass="docutils literal notranslate"><spanclass="pre">mygame/typeclasses/scripts.py</span></code> is an empty <codeclass="docutils literal notranslate"><spanclass="pre">Script</span></code> class already set up. You
can use this as a base for your own scripts.</p>
<divclass="highlight-python notranslate"><divclass="highlight"><pre><span></span><spanclass="c1"># in mygame/typeclasses/scripts.py</span>
<p>Note that if you give keyword arguments to <codeclass="docutils literal notranslate"><spanclass="pre">create_script</span></code> you can override the values
you set in your <codeclass="docutils literal notranslate"><spanclass="pre">at_script_creation</span></code>:</p>
<p>See the <aclass="reference internal"href="../api/evennia.utils.create.html#evennia.utils.create.create_script"title="evennia.utils.create.create_script"><spanclass="xref myst py py-func">create_script</span></a> and <aclass="reference internal"href="../api/evennia.utils.search.html#evennia.utils.search.search_script"title="evennia.utils.search.search_script"><spanclass="xref myst py py-func">search_script</span></a> API documentation for more options on creating and finding Scripts.</p>
</section>
<sectionid="timed-script">
<h4>Timed Script<aclass="headerlink"href="#timed-script"title="Permalink to this headline">¶</a></h4>
<p>There are several properties one can set on the Script to control its timer component.</p>
<divclass="highlight-python notranslate"><divclass="highlight"><pre><span></span><spanclass="c1"># in mygame/typeclasses/scripts.py</span>
<spanclass="bp">self</span><spanclass="o">.</span><spanclass="n">desc</span><spanclass="o">=</span><spanclass="s2">"An example script"</span>
<spanclass="bp">self</span><spanclass="o">.</span><spanclass="n">interval</span><spanclass="o">=</span><spanclass="mi">60</span><spanclass="c1"># 1 min repeat</span>
<p>This example will call <codeclass="docutils literal notranslate"><spanclass="pre">at_repeat</span></code> every minute. The <codeclass="docutils literal notranslate"><spanclass="pre">create_script</span></code> function has an <codeclass="docutils literal notranslate"><spanclass="pre">autostart=True</span></code> keyword
set by default - this means the script’s timer component will be started automatically. Otherwise
<codeclass="docutils literal notranslate"><spanclass="pre">.start()</span></code> must be called separately.</p>
<p>Supported properties are:</p>
<ulclass="simple">
<li><p><codeclass="docutils literal notranslate"><spanclass="pre">key</span></code> (str): The name of the script. This makes it easier to search for it later. If it’s a script
attached to another object one can also get all scripts off that object and get the script that way.</p></li>
<li><p><codeclass="docutils literal notranslate"><spanclass="pre">desc</span></code> (str): Note - not <codeclass="docutils literal notranslate"><spanclass="pre">.db.desc</span></code>! This is a database field on the Script shown in script listings
to help identifying what does what.</p></li>
<li><p><codeclass="docutils literal notranslate"><spanclass="pre">interval</span></code> (int): The amount of time (in seconds) between every ‘tick’ of the timer. Note that
it’s generally bad practice to use sub-second timers for anything in a text-game - the player will
not be able to appreciate the precision (and if you print it, it will just spam the screen). For
calculations you can pretty much always do them on-demand, or at a much slower interval without the player being the wiser.</p></li>
<li><p><codeclass="docutils literal notranslate"><spanclass="pre">start_delay</span></code> (bool): If timer should start right away or wait <codeclass="docutils literal notranslate"><spanclass="pre">interval</span></code> seconds first.</p></li>
<li><p><codeclass="docutils literal notranslate"><spanclass="pre">repeats</span></code> (int): If >0, the timer will only run this many times before stopping. Otherwise the
number of repeats are infinite. If set to 1, the Script mimics a <codeclass="docutils literal notranslate"><spanclass="pre">delay</span></code> action.</p></li>
<li><p><codeclass="docutils literal notranslate"><spanclass="pre">persistent</span></code> (bool): This defaults to <codeclass="docutils literal notranslate"><spanclass="pre">True</span></code> and means the timer will survive a server reload/reboot.
If not, a reload will have the timer come back in a stopped state. Setting this to <codeclass="docutils literal notranslate"><spanclass="pre">False</span></code> will <em>not</em>
delete the Script object itself (use <codeclass="docutils literal notranslate"><spanclass="pre">.delete()</span></code> for this).</p></li>
</ul>
<p>The timer component is controlled with methods on the Script class:</p>
<ulclass="simple">
<li><p><codeclass="docutils literal notranslate"><spanclass="pre">.at_repeat()</span></code> - this method is called every <codeclass="docutils literal notranslate"><spanclass="pre">interval</span></code> seconds while the timer is
active.</p></li>
<li><p><codeclass="docutils literal notranslate"><spanclass="pre">.is_valid()</span></code> - this method is called by the timer just before <codeclass="docutils literal notranslate"><spanclass="pre">at_repeat()</span></code>. If it returns <codeclass="docutils literal notranslate"><spanclass="pre">False</span></code>
the timer is immediately stopped.</p></li>
<li><p><codeclass="docutils literal notranslate"><spanclass="pre">.start()</span></code> - start/update the timer. If keyword arguments are given, they can be used to
change <codeclass="docutils literal notranslate"><spanclass="pre">interval</span></code>, <codeclass="docutils literal notranslate"><spanclass="pre">start_delay</span></code> etc on the fly. This calls the <codeclass="docutils literal notranslate"><spanclass="pre">.at_start()</span></code> hook.
This is also called after a server reload assuming the timer was not previously stopped.</p></li>
<li><p><codeclass="docutils literal notranslate"><spanclass="pre">.update()</span></code> - legacy alias for <codeclass="docutils literal notranslate"><spanclass="pre">.start</span></code>.</p></li>
<li><p><codeclass="docutils literal notranslate"><spanclass="pre">.stop()</span></code> - stops and resets the timer. This calls the <codeclass="docutils literal notranslate"><spanclass="pre">.at_stop()</span></code> hook.</p></li>
<li><p><codeclass="docutils literal notranslate"><spanclass="pre">.pause()</span></code> - pauses the timer where it is, storing its current position. This calls
the <codeclass="docutils literal notranslate"><spanclass="pre">.at_pause(manual_pause=True)</span></code> hook. This is also called on a server reload/reboot,
at which time the <codeclass="docutils literal notranslate"><spanclass="pre">manual_pause</span></code> will be <codeclass="docutils literal notranslate"><spanclass="pre">False</span></code>.</p></li>
<li><p><codeclass="docutils literal notranslate"><spanclass="pre">.unpause()</span></code> - unpause a previously paused script. This will call the <codeclass="docutils literal notranslate"><spanclass="pre">at_start</span></code> hook.</p></li>
<li><p><codeclass="docutils literal notranslate"><spanclass="pre">.time_until_next_repeat()</span></code> - get the time until next time the timer fires.</p></li>
<li><p><codeclass="docutils literal notranslate"><spanclass="pre">.remaining_repeats()</span></code> - get the number of repeats remaining, or <codeclass="docutils literal notranslate"><spanclass="pre">None</span></code> if repeats are infinite.</p></li>
<li><p><codeclass="docutils literal notranslate"><spanclass="pre">.reset_callcount()</span></code> - this resets the repeat counter to start over from 0. Only useful if <codeclass="docutils literal notranslate"><spanclass="pre">repeats>0</span></code>.</p></li>
<li><p><codeclass="docutils literal notranslate"><spanclass="pre">.force_repeat()</span></code> - this prematurely forces <codeclass="docutils literal notranslate"><spanclass="pre">at_repeat</span></code> to be called right away. Doing so will reset the countdown so that next call will again happen after <codeclass="docutils literal notranslate"><spanclass="pre">interval</span></code> seconds.</p></li>
</ul>
</section>
</section>
<sectionid="script-timers-vs-delay-repeat">
<h3>Script timers vs delay/repeat<aclass="headerlink"href="#script-timers-vs-delay-repeat"title="Permalink to this headline">¶</a></h3>
<p>If the <em>only</em> goal is to get a repeat/delay effect, the <aclass="reference internal"href="../api/evennia.utils.utils.html#evennia.utils.utils.delay"title="evennia.utils.utils.delay"><spanclass="xref myst py py-func">evennia.utils.delay</span></a> and <aclass="reference internal"href="../api/evennia.utils.utils.html#evennia.utils.utils.repeat"title="evennia.utils.utils.repeat"><spanclass="xref myst py py-func">evennia.utils.repeat</span></a> functions should generally be considered first. A Script is a lot ‘heavier’ to create/delete on the fly. In fact, for making a single delayed call (<codeclass="docutils literal notranslate"><spanclass="pre">script.repeats==1</span></code>), the <codeclass="docutils literal notranslate"><spanclass="pre">utils.delay</span></code> call is probably always the better choice.</p>
<p>For repeating tasks, the <codeclass="docutils literal notranslate"><spanclass="pre">utils.repeat</span></code> is optimized for quick repeating of a large number of objects. It uses the TickerHandler under the hood. Its subscription-based model makes it very efficient to start/stop the repeating action for an object. The side effect is however that all objects set to tick at a given interval will <em>all do so at the same time</em>. This may or may not look strange in-game depending on the situation. By contrast the Script uses its own ticker that will operate independently from the tickers of all other Scripts.</p>
<p>It’s also worth noting that once the script object has <em>already been created</em>, starting/stopping/pausing/unpausing the timer has very little overhead. The pause/unpause and update methods of the script also offers a bit more fine-control than using <codeclass="docutils literal notranslate"><spanclass="pre">utils.delays/repeat</span></code>.</p>
</section>
<sectionid="script-attached-to-another-object">
<h3>Script attached to another object<aclass="headerlink"href="#script-attached-to-another-object"title="Permalink to this headline">¶</a></h3>
<p>Scripts can be attached to an <aclass="reference internal"href="Accounts.html"><spanclass="doc std std-doc">Account</span></a> or (more commonly) an <aclass="reference internal"href="Objects.html"><spanclass="doc std std-doc">Object</span></a>.
If so, the ‘parent object’ will be available to the script as either <codeclass="docutils literal notranslate"><spanclass="pre">.obj</span></code> or <codeclass="docutils literal notranslate"><spanclass="pre">.account</span></code>.</p>
<spanclass="bp">self</span><spanclass="o">.</span><spanclass="n">desc</span><spanclass="o">=</span><spanclass="s2">"Gives random weather messages."</span>
<spanclass="bp">self</span><spanclass="o">.</span><spanclass="n">interval</span><spanclass="o">=</span><spanclass="mi">60</span><spanclass="o">*</span><spanclass="mi">5</span><spanclass="c1"># every 5 minutes</span>
<div><p>Note that <codeclass="docutils literal notranslate"><spanclass="pre">typeclasses</span></code> in your game dir is added to the setting <codeclass="docutils literal notranslate"><spanclass="pre">TYPECLASS_PATHS</span></code>.
Therefore we don’t need to give the full path (<codeclass="docutils literal notranslate"><spanclass="pre">typeclasses.scripts.Weather</span></code>
but only <codeclass="docutils literal notranslate"><spanclass="pre">scripts.Weather</span></code> above.</p>
</div></blockquote>
<p>You can also attach the script as part of creating it:</p>
<h3>Other Script methods<aclass="headerlink"href="#other-script-methods"title="Permalink to this headline">¶</a></h3>
<p>A Script has all the properties of a typeclassed object, such as <codeclass="docutils literal notranslate"><spanclass="pre">db</span></code> and <codeclass="docutils literal notranslate"><spanclass="pre">ndb</span></code>(see
<aclass="reference internal"href="Typeclasses.html"><spanclass="doc std std-doc">Typeclasses</span></a>). Setting <codeclass="docutils literal notranslate"><spanclass="pre">key</span></code> is useful in order to manage scripts (delete them by name
etc). These are usually set up in the Script’s typeclass, but can also be assigned on the fly as
keyword arguments to <codeclass="docutils literal notranslate"><spanclass="pre">evennia.create_script</span></code>.</p>
<ulclass="simple">
<li><p><codeclass="docutils literal notranslate"><spanclass="pre">at_script_creation()</span></code> - this is only called once - when the script is first created.</p></li>
<li><p><codeclass="docutils literal notranslate"><spanclass="pre">at_server_reload()</span></code> - this is called whenever the server is warm-rebooted (e.g. with the <codeclass="docutils literal notranslate"><spanclass="pre">reload</span></code> command). It’s a good place to save non-persistent data you might want to survive a reload.</p></li>
<li><p><codeclass="docutils literal notranslate"><spanclass="pre">at_server_shutdown()</span></code> - this is called when a system reset or systems shutdown is invoked.</p></li>
<li><p><codeclass="docutils literal notranslate"><spanclass="pre">at_server_start()</span></code> - this is called when the server comes back (from reload/shutdown/reboot). It can be usuful for initializations and caching of non-persistent data when starting up a script’s functionality.</p></li>
<li><p><codeclass="docutils literal notranslate"><spanclass="pre">delete()</span></code> - same as for other typeclassed entities, this will delete the Script. Of note is that
it will also stop the timer (if it runs), leading to the <codeclass="docutils literal notranslate"><spanclass="pre">at_stop</span></code> hook being called.</p></li>
</ul>
<p>In addition, Scripts support <aclass="reference internal"href="Attributes.html"><spanclass="doc std std-doc">Attributes</span></a>, <aclass="reference internal"href="Tags.html"><spanclass="doc std std-doc">Tags</span></a> and <aclass="reference internal"href="Locks.html"><spanclass="doc std std-doc">Locks</span></a> etc like other Typeclassed entities.</p>
<p>See also the methods involved in controlling a <aclass="reference internal"href="#timed-script"><spanclass="std std-doc">Timed Script</span></a> above.</p>
</section>
<sectionid="dealing-with-script-errors">
<h3>Dealing with Script Errors<aclass="headerlink"href="#dealing-with-script-errors"title="Permalink to this headline">¶</a></h3>
<p>Errors inside a timed, executing script can sometimes be rather terse or point to parts of the execution mechanism that is hard to interpret. One way to make it easier to debug scripts is to import Evennia’s native logger and wrap your functions in a try/catch block. Evennia’s logger can show you where the traceback occurred in your script.</p>
<h2>Using GLOBAL_SCRIPTS<aclass="headerlink"href="#using-global-scripts"title="Permalink to this headline">¶</a></h2>
<p>A Script not attached to another entity is commonly referred to as a <em>Global</em> script since it’t available
to access from anywhere. This means they need to be searched for in order to be used.</p>
<p>Evennia supplies a convenient “container” <codeclass="docutils literal notranslate"><spanclass="pre">evennia.GLOBAL_SCRIPTS</span></code> to help organize your global
scripts. All you need is the Script’s <codeclass="docutils literal notranslate"><spanclass="pre">key</span></code>.</p>
<p>Note that global scripts appear as properties on <codeclass="docutils literal notranslate"><spanclass="pre">GLOBAL_SCRIPTS</span></code> based on their <codeclass="docutils literal notranslate"><spanclass="pre">key</span></code>. If you were to create two global scripts with the same <codeclass="docutils literal notranslate"><spanclass="pre">key</span></code> (even with different typeclasses), the <codeclass="docutils literal notranslate"><spanclass="pre">GLOBAL_SCRIPTS</span></code> container will only return one of them (which one depends on order in the database). Best is to organize your scripts so that this does not happen. Otherwise, use <codeclass="docutils literal notranslate"><spanclass="pre">evennia.search_script</span></code> to get exactly the script you want.</p>
</div>
<p>There are two ways to make a script appear as a property on <codeclass="docutils literal notranslate"><spanclass="pre">GLOBAL_SCRIPTS</span></code>:</p>
<olclass="simple">
<li><p>Manually create a new global script with a <codeclass="docutils literal notranslate"><spanclass="pre">key</span></code> using <codeclass="docutils literal notranslate"><spanclass="pre">create_script</span></code>.</p></li>
<li><p>Define the script’s properties in the <codeclass="docutils literal notranslate"><spanclass="pre">GLOBAL_SCRIPTS</span></code> settings variable. This tells Evennia
that it should check if a script with that <codeclass="docutils literal notranslate"><spanclass="pre">key</span></code> exists and if not, create it for you.
This is very useful for scripts that must always exist and/or should be auto-created
when your server restarts. If you use this method, you must make sure all
script keys are globally unique.</p></li>
</ol>
<p>Here’s how to tell Evennia to manage the script in settings:</p>
<divclass="highlight-python notranslate"><divclass="highlight"><pre><span></span><spanclass="c1"># in mygame/server/conf/settings.py</span>
<p>Above we add two scripts with keys <codeclass="docutils literal notranslate"><spanclass="pre">myscript</span></code> and <codeclass="docutils literal notranslate"><spanclass="pre">storagescript</span></code>respectively. The following dict can be empty - the <codeclass="docutils literal notranslate"><spanclass="pre">settings.BASE_SCRIPT_TYPECLASS</span></code> will then be used. Under the hood, the provided dict (along with the <codeclass="docutils literal notranslate"><spanclass="pre">key</span></code>) will be passed into <codeclass="docutils literal notranslate"><spanclass="pre">create_script</span></code> automatically, so all the <aclass="reference internal"href="../api/evennia.utils.create.html#evennia.utils.create.create_script"title="evennia.utils.create.create_script"><spanclass="xref myst py py-func">same keyword arguments as for create_script</span></a> are supported here.</p>
<divclass="admonition warning">
<pclass="admonition-title">Warning</p>
<p>Before setting up Evennia to manage your script like this, make sure that your Script typeclass does not have any critical errors (test it separately). If there are, you’ll see errors in your log and your Script will temporarily fall back to being a <codeclass="docutils literal notranslate"><spanclass="pre">DefaultScript</span></code> type.</p>
</div>
<p>Moreover, a script defined this way is <em>guaranteed</em> to exist when you try to access it:</p>
<p>That is, if the script is deleted, next time you get it from <codeclass="docutils literal notranslate"><spanclass="pre">GLOBAL_SCRIPTS</span></code>, Evennia will use the
information in settings to recreate it for you on the fly.</p>
<pclass="last">You are reading an old version of the Evennia documentation. <ahref="https://www.evennia.com/docs/latest/index.html">The latest version is here</a></p>.