2022-11-14 22:43:45 +01:00
<!DOCTYPE html>
< html >
< head >
< meta charset = "utf-8" / >
< meta name = "viewport" content = "width=device-width, initial-scale=1.0" / > < meta name = "generator" content = "Docutils 0.17.1: http://docutils.sourceforge.net/" / >
2023-10-19 20:22:27 +00:00
< title > Adding Command Cooldowns — Evennia 2.x documentation< / title >
2022-11-14 22:43:45 +01:00
< 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" / >
2022-11-21 23:18:31 +00:00
< link rel = "next" title = "Commands that take time to finish" href = "Howto-Command-Duration.html" / >
< link rel = "prev" title = "Adding a Command Prompt" href = "Howto-Command-Prompt.html" / >
2022-11-14 22:43:45 +01:00
< / head > < body >
2023-10-19 20:22:27 +00:00
2022-11-14 22:43:45 +01:00
< div class = "related" role = "navigation" aria-label = "related navigation" >
< h3 > Navigation< / h3 >
< ul >
< li class = "right" style = "margin-right: 10px" >
< a href = "../genindex.html" title = "General Index"
accesskey="I">index< / a > < / li >
< li class = "right" >
< a href = "../py-modindex.html" title = "Python Module Index"
>modules< / a > |< / li >
< li class = "right" >
2022-11-21 23:18:31 +00:00
< a href = "Howto-Command-Duration.html" title = "Commands that take time to finish"
2022-11-14 22:43:45 +01:00
accesskey="N">next< / a > |< / li >
< li class = "right" >
2022-11-21 23:18:31 +00:00
< a href = "Howto-Command-Prompt.html" title = "Adding a Command Prompt"
2022-11-14 22:43:45 +01:00
accesskey="P">previous< / a > |< / li >
2023-10-19 20:22:27 +00:00
< li class = "nav-item nav-item-0" > < a href = "../index.html" > Evennia 2.x< / a > » < / li >
< li class = "nav-item nav-item-1" > < a href = "Howtos-Overview.html" accesskey = "U" > Tutorials and How-To’ s< / a > » < / li >
2022-11-21 23:18:31 +00:00
< li class = "nav-item nav-item-this" > < a href = "" > Adding Command Cooldowns< / a > < / li >
2022-11-14 22:43:45 +01:00
< / ul >
2023-06-10 08:53:19 +00:00
< / div >
2022-11-14 22:43:45 +01:00
< div class = "document" >
< div class = "documentwrapper" >
< div class = "sphinxsidebar" role = "navigation" aria-label = "main navigation" >
< div class = "sphinxsidebarwrapper" >
< p class = "logo" > < a href = "../index.html" >
< img class = "logo" src = "../_static/evennia_logo.png" alt = "Logo" / >
< / a > < / p >
< div id = "searchbox" style = "display: none" role = "search" >
< h3 id = "searchlabel" > Quick search< / h3 >
< div class = "searchformwrapper" >
< form class = "search" action = "../search.html" method = "get" >
< input type = "text" name = "q" aria-labelledby = "searchlabel" / >
< input type = "submit" value = "Go" / >
< / form >
< / div >
< / div >
< script > $ ( '#searchbox' ) . show ( 0 ) ; < / script >
< h3 > < a href = "../index.html" > Table of Contents< / a > < / h3 >
< ul >
2022-11-21 23:18:31 +00:00
< li > < a class = "reference internal" href = "#" > Adding Command Cooldowns< / a > < ul >
< li > < a class = "reference internal" href = "#an-efficient-cooldown" > An efficient cooldown< / a > < ul >
< li > < a class = "reference internal" href = "#non-persistent-cooldown" > Non-Persistent cooldown< / a > < / li >
< / ul >
< / li >
2022-11-14 22:43:45 +01:00
< li > < a class = "reference internal" href = "#make-a-cooldown-aware-command-parent" > Make a cooldown-aware command parent< / a > < ul >
< li > < a class = "reference internal" href = "#command-crossover" > Command crossover< / a > < / li >
< / ul >
< / li >
< / ul >
< / li >
< / ul >
< h4 > Previous topic< / h4 >
2022-11-20 23:39:28 +00:00
< p class = "topless" > < a href = "Howto-Command-Prompt.html"
2022-11-21 23:18:31 +00:00
title="previous chapter">Adding a Command Prompt< / a > < / p >
2022-11-14 22:43:45 +01:00
< h4 > Next topic< / h4 >
2022-11-20 23:39:28 +00:00
< p class = "topless" > < a href = "Howto-Command-Duration.html"
2022-11-21 23:18:31 +00:00
title="next chapter">Commands that take time to finish< / a > < / p >
2022-11-14 22:43:45 +01:00
< div role = "note" aria-label = "source link" >
<!-- h3>This Page</h3 -->
< ul class = "this-page-menu" >
2022-11-20 23:39:28 +00:00
< li > < a href = "../_sources/Howtos/Howto-Command-Cooldown.md.txt"
2022-11-14 22:43:45 +01:00
rel="nofollow">Show Page Source< / a > < / li >
< / ul >
< / div > < h3 > Links< / h3 >
< ul >
2023-10-19 20:22:27 +00:00
< 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 >
2022-11-14 22:43:45 +01:00
< 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 >
2023-10-19 20:22:27 +00:00
< h3 > Doc Versions< / h3 >
2022-11-14 22:43:45 +01:00
< ul >
2023-10-19 20:22:27 +00:00
< li > < a href = "Howto-Command-Cooldown.html" > 2.x (main branch)< / a > < / li >
2022-11-15 20:29:38 +00:00
< ul >
2023-10-19 20:22:27 +00:00
< li > < a href = "../1.3.0/index.html" > 1.3.0 (v1.3.0 branch)< / a > < / li >
< li > < a href = "../0.9.5/index.html" > 0.9.5 (v0.9.5 branch)< / a > < / li >
2022-11-15 20:29:38 +00:00
2022-11-14 22:43:45 +01:00
< / ul >
< / div >
< / div >
< div class = "bodywrapper" >
< div class = "body" role = "main" >
2022-11-21 23:18:31 +00:00
< section class = "tex2jax_ignore mathjax_ignore" id = "adding-command-cooldowns" >
< h1 > Adding Command Cooldowns< a class = "headerlink" href = "#adding-command-cooldowns" title = "Permalink to this headline" > ¶< / a > < / h1 >
< div class = "highlight-none notranslate" > < div class = "highlight" > < pre > < span > < / span > > hit goblin with sword
You strike goblin with the sword. It dodges!
> hit goblin with sword
You are off-balance and can' t attack again yet.
< / pre > < / div >
< / div >
2022-11-14 22:43:45 +01:00
< p > Some types of games want to limit how often a command can be run. If a
character casts the spell < em > Firestorm< / em > , you might not want them to spam that
2022-11-21 23:18:31 +00:00
command over and over. In an advanced combat system, a massive swing may
2022-11-14 22:43:45 +01:00
offer a chance of lots of damage at the cost of not being able to re-do it for
2022-11-21 23:18:31 +00:00
a while.< / p >
< p > Such effects are called < em > command cooldowns< / em > .< / p >
< aside class = "sidebar" >
< p > The < a class = "reference internal" href = "../Contribs/Contrib-Cooldowns.html" > < span class = "doc std std-doc" > Cooldown contrib< / span > < / a > is a ready-made solution for command cooldowns. It is based on this howto and implements a < span class = "xref myst" > handler< / span > on the object to conveniently manage and store the cooldowns.< / p >
< / aside >
< p > This howto exemplifies a very resource-efficient way to do cooldowns. A more
‘ active’ way is to use asynchronous delays as in the < span class = "xref myst" > Command-Duration howto< / span > suggests. The two howto’ s might be useful to combine if you want to echo some message to the user after the cooldown ends.< / p >
< section id = "an-efficient-cooldown" >
< h2 > An efficient cooldown< a class = "headerlink" href = "#an-efficient-cooldown" title = "Permalink to this headline" > ¶< / a > < / h2 >
< p > The idea is that when a < a class = "reference internal" href = "../Components/Commands.html" > < span class = "doc std std-doc" > Command< / span > < / a > runs, we store the time it runs. When it next runs, we check again the current time. The command is only allowed to run if enough time passed since now and the previous run. This is a < em > very< / em > efficient implementation that only checks on-demand.< / p >
2022-11-14 22:43:45 +01:00
< div class = "highlight-python notranslate" > < div class = "highlight" > < pre > < span > < / span > < span class = "c1" > # in, say, mygame/commands/spells.py< / span >
< span class = "kn" > import< / span > < span class = "nn" > time< / span >
< span class = "kn" > from< / span > < span class = "nn" > evennia< / span > < span class = "kn" > import< / span > < span class = "n" > default_cmds< / span >
< span class = "k" > class< / span > < span class = "nc" > CmdSpellFirestorm< / span > < span class = "p" > (< / span > < span class = "n" > default_cmds< / span > < span class = "o" > .< / span > < span class = "n" > MuxCommand< / span > < span class = "p" > ):< / span >
2023-01-06 17:19:13 +00:00
< span class = "w" > < / span > < span class = "sd" > " " " < / span >
2022-11-14 22:43:45 +01:00
< span class = "sd" > Spell - Firestorm< / span >
< span class = "sd" > Usage:< / span >
< span class = "sd" > cast firestorm < target> < / span >
< span class = "sd" > This will unleash a storm of flame. You can only release one< / span >
< span class = "sd" > firestorm every five minutes (assuming you have the mana).< / span >
< span class = "sd" > " " " < / span >
< span class = "n" > key< / span > < span class = "o" > =< / span > < span class = "s2" > " cast firestorm" < / span >
< span class = "n" > rate_of_fire< / span > < span class = "o" > =< / span > < span class = "mi" > 60< / span > < span class = "o" > *< / span > < span class = "mi" > 2< / span > < span class = "c1" > # 2 minutes< / span >
< span class = "k" > def< / span > < span class = "nf" > func< / span > < span class = "p" > (< / span > < span class = "bp" > self< / span > < span class = "p" > ):< / span >
< span class = "s2" > " Implement the spell" < / span >
< span class = "n" > now< / span > < span class = "o" > =< / span > < span class = "n" > time< / span > < span class = "o" > .< / span > < span class = "n" > time< / span > < span class = "p" > ()< / span >
2022-11-21 23:18:31 +00:00
< span class = "n" > last_cast< / span > < span class = "o" > =< / span > < span class = "n" > caller< / span > < span class = "o" > .< / span > < span class = "n" > db< / span > < span class = "o" > .< / span > < span class = "n" > firestorm_last_cast< / span > < span class = "c1" > # could be None< / span >
2022-11-14 22:43:45 +01:00
< span class = "k" > if< / span > < span class = "n" > last_cast< / span > < span class = "ow" > and< / span > < span class = "p" > (< / span > < span class = "n" > now< / span > < span class = "o" > -< / span > < span class = "n" > last_cast< / span > < span class = "o" > < < / span > < span class = "bp" > self< / span > < span class = "o" > .< / span > < span class = "n" > rate_of_fire< / span > < span class = "p" > ):< / span >
< span class = "n" > message< / span > < span class = "o" > =< / span > < span class = "s2" > " You cannot cast this spell again yet." < / span >
< span class = "bp" > self< / span > < span class = "o" > .< / span > < span class = "n" > caller< / span > < span class = "o" > .< / span > < span class = "n" > msg< / span > < span class = "p" > (< / span > < span class = "n" > message< / span > < span class = "p" > )< / span >
< span class = "k" > return< / span >
< span class = "c1" > # [the spell effect is implemented]< / span >
< span class = "c1" > # if the spell was successfully cast, store the casting time< / span >
2022-11-21 23:18:31 +00:00
< span class = "bp" > self< / span > < span class = "o" > .< / span > < span class = "n" > caller< / span > < span class = "o" > .< / span > < span class = "n" > db< / span > < span class = "o" > .< / span > < span class = "n" > firestorm_last_cast< / span > < span class = "o" > =< / span > < span class = "n" > now< / span >
< / pre > < / div >
< / div >
< p > We specify < code class = "docutils literal notranslate" > < span class = "pre" > rate_of_fire< / span > < / code > and then just check for an < a class = "reference internal" href = "../Components/Attributes.html" > < span class = "doc std std-doc" > Attribute< / span > < / a > < code class = "docutils literal notranslate" > < span class = "pre" > firestorm_last_cast< / span > < / code > on the < code class = "docutils literal notranslate" > < span class = "pre" > caller.< / span > < / code > It is either < code class = "docutils literal notranslate" > < span class = "pre" > None< / span > < / code > (because the spell was never cast before) or an timestamp representing the last time the spell was cast.< / p >
< section id = "non-persistent-cooldown" >
< h3 > Non-Persistent cooldown< a class = "headerlink" href = "#non-persistent-cooldown" title = "Permalink to this headline" > ¶< / a > < / h3 >
< p > The above implementation will survive a reload. If you don’ t want that, you can just switch to let < code class = "docutils literal notranslate" > < span class = "pre" > firestorm_last_cast< / span > < / code > be a < a class = "reference internal" href = "../Components/Attributes.html#in-memory-attributes-nattributes" > < span class = "std std-doc" > NAtrribute< / span > < / a > instead. For example:< / p >
< div class = "highlight-python notranslate" > < div class = "highlight" > < pre > < span > < / span > < span class = "n" > last_cast< / span > < span class = "o" > =< / span > < span class = "n" > caller< / span > < span class = "o" > .< / span > < span class = "n" > ndb< / span > < span class = "o" > .< / span > < span class = "n" > firestorm_last_cast< / span >
< span class = "c1" > # ... < / span >
< span class = "bp" > self< / span > < span class = "o" > .< / span > < span class = "n" > caller< / span > < span class = "o" > .< / span > < span class = "n" > ndb< / span > < span class = "o" > .< / span > < span class = "n" > firestorm_last_cast< / span > < span class = "o" > =< / span > < span class = "n" > now< / span >
2022-11-14 22:43:45 +01:00
< / pre > < / div >
< / div >
2022-11-21 23:18:31 +00:00
< p > That is, use < code class = "docutils literal notranslate" > < span class = "pre" > .ndb< / span > < / code > instead of < code class = "docutils literal notranslate" > < span class = "pre" > .db< / span > < / code > . Since a < code class = "docutils literal notranslate" > < span class = "pre" > NAttribute< / span > < / code > s are purely in-memory, they can be faster to read and write to than an < code class = "docutils literal notranslate" > < span class = "pre" > Attribute< / span > < / code > . So this can be more optimal if your intervals are short and need to change often. The drawback is that they’ ll reset if the server reloads.< / p >
2022-11-14 22:43:45 +01:00
< / section >
< / section >
< section id = "make-a-cooldown-aware-command-parent" >
< h2 > Make a cooldown-aware command parent< a class = "headerlink" href = "#make-a-cooldown-aware-command-parent" title = "Permalink to this headline" > ¶< / a > < / h2 >
< p > If you have many different spells or other commands with cooldowns, you don’ t
want to have to add this code every time. Instead you can make a “cooldown
command mixin” class. A < em > mixin< / em > is a class that you can ‘ add’ to another class
(via multiple inheritance) to give it some special ability. Here’ s an example
with persistent storage:< / p >
< div class = "highlight-python notranslate" > < div class = "highlight" > < pre > < span > < / span > < span class = "c1" > # in, for example, mygame/commands/mixins.py< / span >
< span class = "kn" > import< / span > < span class = "nn" > time< / span >
< span class = "k" > class< / span > < span class = "nc" > CooldownCommandMixin< / span > < span class = "p" > :< / span >
< span class = "n" > rate_of_fire< / span > < span class = "o" > =< / span > < span class = "mi" > 60< / span >
< span class = "n" > cooldown_storage_key< / span > < span class = "o" > =< / span > < span class = "s2" > " last_used" < / span >
< span class = "n" > cooldown_storage_category< / span > < span class = "o" > =< / span > < span class = "s2" > " cmd_cooldowns" < / span >
< span class = "k" > def< / span > < span class = "nf" > check_cooldown< / span > < span class = "p" > (< / span > < span class = "bp" > self< / span > < span class = "p" > ):< / span >
< span class = "n" > last_time< / span > < span class = "o" > =< / span > < span class = "bp" > self< / span > < span class = "o" > .< / span > < span class = "n" > caller< / span > < span class = "o" > .< / span > < span class = "n" > attributes< / span > < span class = "o" > .< / span > < span class = "n" > get< / span > < span class = "p" > (< / span >
< span class = "n" > key< / span > < span class = "o" > =< / span > < span class = "bp" > self< / span > < span class = "o" > .< / span > < span class = "n" > cooldown_storage_key< / span > < span class = "p" > ,< / span >
< span class = "n" > category< / span > < span class = "o" > =< / span > < span class = "bp" > self< / span > < span class = "o" > .< / span > < span class = "n" > cooldown_storage_category< / span > < span class = "p" > )< / span >
< span class = "p" > )< / span >
< span class = "k" > return< / span > < span class = "p" > (< / span > < span class = "n" > time< / span > < span class = "o" > .< / span > < span class = "n" > time< / span > < span class = "p" > ()< / span > < span class = "o" > -< / span > < span class = "n" > last_time< / span > < span class = "p" > )< / span > < span class = "o" > < < / span > < span class = "bp" > self< / span > < span class = "o" > .< / span > < span class = "n" > rate_of_fire< / span >
< span class = "k" > def< / span > < span class = "nf" > update_cooldown< / span > < span class = "p" > (< / span > < span class = "bp" > self< / span > < span class = "p" > ):< / span >
< span class = "bp" > self< / span > < span class = "o" > .< / span > < span class = "n" > caller< / span > < span class = "o" > .< / span > < span class = "n" > attribute< / span > < span class = "o" > .< / span > < span class = "n" > add< / span > < span class = "p" > (< / span >
< span class = "n" > key< / span > < span class = "o" > =< / span > < span class = "bp" > self< / span > < span class = "o" > .< / span > < span class = "n" > cooldown_storage_key< / span > < span class = "p" > ,< / span >
< span class = "n" > value< / span > < span class = "o" > =< / span > < span class = "n" > time< / span > < span class = "o" > .< / span > < span class = "n" > time< / span > < span class = "p" > (),< / span >
< span class = "n" > category< / span > < span class = "o" > =< / span > < span class = "bp" > self< / span > < span class = "o" > .< / span > < span class = "n" > cooldown_storage_category< / span >
< span class = "p" > )< / span >
< / pre > < / div >
< / div >
< p > This is meant to be mixed into a Command, so we assume < code class = "docutils literal notranslate" > < span class = "pre" > self.caller< / span > < / code > exists.
We allow for setting what Attribute key/category to use to store the cooldown.< / p >
< p > It also uses an Attribute-category to make sure what it stores is not mixed up
with other Attributes on the caller.< / p >
< p > Here’ s how it’ s used:< / p >
< div class = "highlight-python notranslate" > < div class = "highlight" > < pre > < span > < / span > < span class = "c1" > # in, say, mygame/commands/spells.py< / span >
< span class = "kn" > from< / span > < span class = "nn" > evennia< / span > < span class = "kn" > import< / span > < span class = "n" > default_cmds< / span >
< span class = "kn" > from< / span > < span class = "nn" > .mixins< / span > < span class = "kn" > import< / span > < span class = "n" > CooldownCommandMixin< / span >
< span class = "k" > class< / span > < span class = "nc" > CmdSpellFirestorm< / span > < span class = "p" > (< / span >
< span class = "n" > CooldownCommandMixin< / span > < span class = "p" > ,< / span > < span class = "n" > default_cmds< / span > < span class = "o" > .< / span > < span class = "n" > MuxCommand< / span > < span class = "p" > ):< / span >
< span class = "n" > key< / span > < span class = "o" > =< / span > < span class = "s2" > " cast firestorm" < / span >
< span class = "n" > cooldown_storage_key< / span > < span class = "o" > =< / span > < span class = "s2" > " firestorm_last_cast" < / span >
< span class = "n" > rate_of_fire< / span > < span class = "o" > =< / span > < span class = "mi" > 60< / span > < span class = "o" > *< / span > < span class = "mi" > 2< / span >
< span class = "k" > def< / span > < span class = "nf" > func< / span > < span class = "p" > (< / span > < span class = "bp" > self< / span > < span class = "p" > ):< / span >
< span class = "k" > if< / span > < span class = "ow" > not< / span > < span class = "bp" > self< / span > < span class = "o" > .< / span > < span class = "n" > check_cooldown< / span > < span class = "p" > ():< / span >
< span class = "bp" > self< / span > < span class = "o" > .< / span > < span class = "n" > caller< / span > < span class = "o" > .< / span > < span class = "n" > msg< / span > < span class = "p" > (< / span > < span class = "s2" > " You cannot cast this spell again yet." < / span > < span class = "p" > )< / span >
< span class = "k" > return< / span >
< span class = "c1" > # [the spell effect happens]< / span >
< span class = "bp" > self< / span > < span class = "o" > .< / span > < span class = "n" > update_cooldown< / span > < span class = "p" > ()< / span >
< / pre > < / div >
< / div >
< p > So the same as before, we have just hidden away the cooldown checks and you can
reuse this mixin for all your cooldowns.< / p >
< section id = "command-crossover" >
< h3 > Command crossover< a class = "headerlink" href = "#command-crossover" title = "Permalink to this headline" > ¶< / a > < / h3 >
< p > This example of cooldown-checking also works < em > between< / em > commands. For example,
you can have all fire-related spells store the cooldown with the same
< code class = "docutils literal notranslate" > < span class = "pre" > cooldown_storage_key< / span > < / code > (like < code class = "docutils literal notranslate" > < span class = "pre" > fire_spell_last_used< / span > < / code > ). That would mean casting
of < em > Firestorm< / em > would block all other fire-related spells for a while.< / p >
2022-11-21 23:18:31 +00:00
< p > Similarly, when you take that big sword swing, other types of attacks could
2022-11-14 22:43:45 +01:00
be blocked before you can recover your balance.< / p >
< / section >
< / section >
< / section >
< / div >
< / div >
< / div >
< / div >
< div class = "related" role = "navigation" aria-label = "related navigation" >
< h3 > Navigation< / h3 >
< ul >
< li class = "right" style = "margin-right: 10px" >
< a href = "../genindex.html" title = "General Index"
>index< / a > < / li >
< li class = "right" >
< a href = "../py-modindex.html" title = "Python Module Index"
>modules< / a > |< / li >
< li class = "right" >
2022-11-21 23:18:31 +00:00
< a href = "Howto-Command-Duration.html" title = "Commands that take time to finish"
2022-11-14 22:43:45 +01:00
>next< / a > |< / li >
< li class = "right" >
2022-11-21 23:18:31 +00:00
< a href = "Howto-Command-Prompt.html" title = "Adding a Command Prompt"
2022-11-14 22:43:45 +01:00
>previous< / a > |< / li >
2023-10-19 20:22:27 +00:00
< li class = "nav-item nav-item-0" > < a href = "../index.html" > Evennia 2.x< / a > » < / li >
< li class = "nav-item nav-item-1" > < a href = "Howtos-Overview.html" > Tutorials and How-To’ s< / a > » < / li >
2022-11-21 23:18:31 +00:00
< li class = "nav-item nav-item-this" > < a href = "" > Adding Command Cooldowns< / a > < / li >
2022-11-14 22:43:45 +01:00
< / ul >
< / div >
2023-10-19 20:22:27 +00:00
2022-11-14 22:43:45 +01:00
< div class = "footer" role = "contentinfo" >
2023-10-19 20:22:27 +00:00
© Copyright 2023, The Evennia developer community.
2022-11-14 22:43:45 +01:00
Created using < a href = "https://www.sphinx-doc.org/" > Sphinx< / a > 3.2.1.
< / div >
< / body >
< / html >