Long Term Memory: Flags¶
Table of Contents
On the previous page you learned about definitions and their usage as short-term memory. On this page, you will learn about flags.
Flags, like definitions, are a form of memory. They are data-points that you can freely control and change within a script, without affecting anything other than your own scripts.
Flags have two key distinctions from definitions:
First, flags are long term memory, while definitions are short term. As you learned on the previous page, a definition only lasts as long the queue does. Flags, however, last much longer - flags can even stay forever if needed.
Second, you can choose what object a flag is linked to. As you'll come to understand after learning about the flag system, definitions are basically a special case of flags that are linked exclusively to queues.
Flags also use the Data Actions system, just like definitions do.
define command, the
flag command always uses data action syntax. Meaning, the most basic set command uses a
: rather than a space, like:
- flag <object> my_flag:my_value.
Before we bother with much of the theory behind flags, let's get some practice trying them out, using the /ex command!
Open up your Minecraft window, and type in this command:
/ex flag server greetable:World
With that command, you have now set a flag named "
greetable" to the value "
World" on the
Now, let's make use of that flag... type in:
/ex narrate "Hello, <server.flag[greetable]>!"
If everything went well, you should see output like the following:
Flags are always attached to an object. In the example above, we used the
server object. The
server object is a special helper object that exists for things like flags to have a global static target. Flags attached to the server stick around forever, and can be accessed from anywhere at any time.
The most common object that flags get attached to is a player. When a flag is attached to a player, that means only that one single player has that flag - any other players would only have it if the flag command is used again to set it on each other player.
Setting a flag on a player is easy - it's just like setting a global flag on the server, you just have to feed the command a specific player instead of
Try typing this command:
/ex flag <player> status:learning
And then type:
/ex narrate "<player.name> is <player.flag[status]> Denizen!"
You should see roughly the following:
If any other players try that
/ex narrate command without doing the
/ex flag first, it will show an error message - because only you have the
status flag, no other players.
The examples above are pretty abstract, so let's use the parts of the flag system we've already learned to create a simple script that looks a little more like something you might actually use.
ore_counter: type: world events: after player breaks iron_ore: - flag player ores_broken:++ - narrate "You have broken <player.flag[ores_broken]> iron ore so far! Keep it up!"
This script will keep a running count of how many iron ore blocks the player has mined. Each player will have their own separate count (this uses the 'increment' data action, which is explained on the definitions page).
You can use the 'clear' data action, marked with a
! symbol, to remove a flag at any time. After using the script example above, try doing
/ex flag player ores_broken:! to remove your own iron ore counter flag, then break another iron ore block. If all went well, you will see the flag tag fills with a value of
1 (indicating that as far as the script knows, this is the first iron ore you've ever mined).
If you want to know whether an object already has a flag or not, the solution is simple:
has_flag! This looks just like the
flag tag, but instead of returning the flag value, will instead return a simple boolean:
You can use this inside an if command like
- if <player.has_flag[ores_broken]>:, to only run the
if's commands when the player already has broken at least one iron ore in the script above.
This is also the basic way to implement values that either are there, or aren't (as opposed to having some additional data, like how
ores_broken has a number associated with it).
toggler_task: type: task script: - if <player.has_flag[my_first_toggle]>: - flag player my_first_toggle:! - narrate "Disabled your toggle." - else: - flag player my_first_toggle - narrate "Enabled your toggle."
The above example script, when ran via
/ex run toggler_task, will toggle the flag
my_first_toggle on or off, by removing it or setting it (if you have no value to give, you can just leave the value off from the flag command), using the
has_flag tag to determine whether to add or remove it.
As you learn about more features like player commands, you can use these for simple yet powerful options, like a command that toggles some flag on or off when used, that then ties into some world event that checks if the player has the flag to determine whether to apply some special effect.
Flags are a core feature of Denizen... this means that they are integrated in a variety of places within Denizen to make things easier when using them.
For example, if you want an event to only fire if the player involved has a specific flag, you can use the
flagged: event switch, like so:
optional_ore_counter: type: world events: after player breaks iron_ore flagged:ore_quest: - flag player ores_broken:++ - narrate "You have broken <player.flag[ores_broken]> iron ore so far! Keep it up!"
This is the ore_counter script from earlier, but it now only runs for players that have the toggleable flag named
ore_quest. Now, rather than just being a generic counter of ores broken, this script has become a part of a quest that requires a player go out and break some number of iron ore blocks to complete it.
There are many features like this throughout Denizen, including the player
flagged: event switch, the
location_flagged: and etc. similar event switches,
(x)_flagged matchables, various command arguments like the
to_flagged argument, a variety of convenience tags like
<server.online_players_flagged[flag_name]>, and so much more. As you learn more about Denizen and eventually start looking through the meta-documentation, you'll encounter many of these.
Flags are persistent. This means that things which normally cause data to go away, like restarting the server, will not get rid of flag data.
There are only four cases in which a flag will go away:
Explicit intentional removal within a script, using the 'clear' data action.
Timed expiration (explained in the next section).
Attached object is gone (for example, the flag is on a mob entity, and that mob dies).
And of course: hard server crash or deletion of server files.
One common usage of flags is as long-term cooldown timers, like a quest or special effect that only works once per day. Here's an example of how that might look:
once_a_day: type: task script: - if <player.has_flag[my_script_cooldown]>: - narrate "You can only get this reward once per day. You must wait <player.flag_expiration[my_script_cooldown].from_now.formatted>." - stop - flag player my_script_cooldown expire:1d - narrate "Here's your daily reward!" - give diamond
This script example will give the player a diamond, but no more often than once per day, using a flag as the cooldown method.
While this might seem a bit pointless in a
task script, after you've gone farther into the guide, you could attach it to an NPC's click handler to make an NPC that can be clicked for a daily reward, it becomes basically a complete feature of your server!
You'll notice two new parts of the flag system being used here:
First, in the
flag command, there is now a
expire:1d argument. This does exactly what you think it does: it makes the flag only stick around for one day... after that, the flag is considered the same as if it didn't exist. The input is a
DurationTag object, which can be written like
1d to mean "one day", or
5h to mean "five hours", or anything like that. The duration value input is then used to calculate the date/time when the flag should expire (by adding the duration onto the current date/time), and stored alongside the flag.
Second, in the
narrate command, we used the
flag_expiration tag. This tag, as the name implies, returns the time at which the flag is scheduled to expire. It does this in the form of a
TimeTag object. We then use the tag
TimeTag.from_now, which gets the
DurationTag representing the distance in time between right now and some previously stored
TimeTag (so at the moment the flag is set, it will be
1d... if you wait an hour, it will be
23h, etc.), followed by the tag
DurationTag.formatted, which converts the raw duration object to something relatively clean for human reading.
Note that flags expirations are something you can check for, but you cannot wait for them - there is no "flag expires" event or similar. If you would like to know why this is, refer to the Flag System technical doc.
Note that very short/simple cooldowns (anything less than a few hours, usually) should often use the
ratelimit command instead of flags, like
- ratelimit <player> 1h. Ratelimits are very simple and easy, but notably do not persist across restarts and do not have the ability to show cooldown messages like the one the script above uses.
You can actually flag a lot of objects! Here are the main ones:
The server itself, as a global flag, using the keyword
Any player, using a
Any Citizens NPC, using an
Any minecraft entity, using an
Any block in the world, using a
Any chunk in the world, using a
Any item - however, items work a little different, so this will be covered in the custom items page.
Any world using a
Any noted area (notes will be covered in the advanced section) using a
Any noted inventory (notes will be covered in the advanced section) using an
Most other object types too. Any type that's in some way uniquely identified will usually be flaggable.
Note that flags are generally loaded when the object is loaded - so server flags are loaded when the server starts, player flags are loaded when the players joins, etc.
With players, if the player is offline when you use flag commands or tags, the player's data will automatically be loaded at that time.
For entities however, if an entity is despawned (meaning it's in a chunk that isn't loaded), there is no way to reliably load that entity, and it will therefore not work.
For blocks, if the chunk that contains the block is not loaded, you will have to load the chunk before working with its flags. For the
flag command, this will be automatic, however for flag tags, there is no automatic chunkload, and you can instead use the
chunkload command beforehand to ensure the chunk is available.
For more information on exactly how different object types handle flags, refer to the Flag System technical doc.
Many basic ASCII symbols (like
% or etc., anything that isn't a letter or number really) in names of things (flags, definitions, script containers, etc) in Denizen can have special meanings and, other than when intentionally using those special meanings, should be avoided. Keep to simple textual names, with underscores
_ to separate words instead of spaces.
Advanced/experienced users might recognize that flags are a form of data-map - that is, a system of data consisting of named keys paired with values. Those users might be interested in
MapTag objects, or in using flags to produce dynamic submapping structures (maps within maps). The Denizen flag systems supports submapping automatically by using the
. symbol as the submap separator, like
- flag server myroot.mysubmap.mykey:myvalue. This will assign the flag named
myroot.mysubmap to a MapTag value of
[mykey=myvalue], and thus the flag named
myroot to a nested-MapTag value of
has_flag tag will return true for
has_flag[myroot], and true for
has_flag[myroot.mysubmap], and true for
If this seems arcane or complex to you, don't worry, you don't need to use or understand this right now. There will be further explanation of this system / how to use it / what it can be used for in a later part of the advanced section.
It should be noted that it is considered restricted to have flag names prefixed with two underscores, like
__name. This is because some internal features of Denizen will generate flags, and use this naming convention to avoid conflicts with scripts. This includes for example the
zap command using
__interact_step and the
cooldown command using
Advanced users may decide they need even more custom memory options. While flags and definitions should cover the overwhelming majority of cases, Denizen does provide additional options (primarily intended for intercompatibility with pre-existing data systems) such as the YAML command and the SQL command.