Your First World Script¶
Table of Contents
So, you've learned how to use '/ex' to run a single Denizen command and how to make a 'task' script to run a series of commands together. Those are pretty useful tools, but they require you sit there and type commands in to make it happen. Isn't the point of a script engine supposed to be that it automatically triggers when needed, to make custom stuff on your server? It would be ridiculous to just sit on a server typing
/ex commands whenever a player needs something!
Let's take a look at the primary way we make automatically triggered scripts:
world script containers!
A world script contains
events. An event is basically: a thing that happens in the world. For example, when a player breaks a block, there's an event for that. Most events are named pretty clearly and simply - for example, the previously mentioned event would be
player breaks block. Note that all events start with either the word
after, and what follows is usually English phrasing of the event based on that. Some more examples of event names:
on player places block,
after entity dies,
on creeper powered,
after lightning strikes, ... (the distinction between
after is explained farther down this page).
my_world_script: type: world events: after player breaks block: - narrate "Whoa <player.name>, you broke a block!"
Go ahead and put that into a script file, use
/ex reload to load it in, then break any block. You'll see the message appear in chat when you do so.
Some things to note in this example:
typefor these scripts is now
script:from before, we now have
events:, which contains within it the actual event name.
The event itself (
after player breaks block) is indented another 4 spaces past where
events:is, which makes the event part of the
eventsblock rather than part of the script's base level keys.
The player that broke the block is automatically the contextually linked player. So, the narrate shows to that player, and
<player.name>will be that players name.
An event will run every time the thing happens in the world. Every single time any player breaks any block, that script will run. If 5 players all break a block at the same time, that script will run 5 times - once for each player, each time linking to the specific player that broke the block that the script is reacting to. If one player gets an efficiency 5 shovel and destroys 10 dirt blocks in under a second... yep, the event will run 10 times, with that one player linked each time.
So, you know that they broke a block when that script runs, but you're of course wondering how you know what type of block was broken (after all, breaking a block of dirt and breaking a block of diamond ore probably shouldn't be handled the exact same way). There are two ways of doing this.
Event names allow extra specification to be added, as either an input filling or an event switch.
The breaks block event is documented as:
player breaks block or
player breaks <material>. The second version here gives us a
<material> input option. In this context, the
<> means "fill in your specific value here". So, try changing your script's event line to be
after player breaks stone:, then reload and try breaking a few different block types, including stone. You will see that the narrate only happens when you break stone. If you break dirt or anything else, nothing will happen.
Event switches are additional options on an event aside from just the basic name input. The breaks block event has a few switches available, including one documented as
with:<item> to only process the event when the player is breaking the block with a specified item.
To put this to use, you can do, for example:
after player breaks stone with:diamond_pickaxe: to make the script only run when the player specifically uses a diamond pickaxe while breaking the stone - if you try it with an iron pickaxe or anything else instead, the script simply won't run.
Note that event lines can never contain tags - they must always be valid plain text. In a later section you will learn how to change whether the script runs based on more dynamic tag-based checks.
There is, however, a bit of limited dynamicness available. For example, consider the
with:diamond_pickaxe switch we used above. What if we want any pickaxe to count? We can use
with:*_pickaxe for this. The
* symbol means "anything here". Similarly, if we want only diamond or iron, we can use
| symbol means "either one of these counts". You can also apply this to the input portions of an event line. For example, if you want to react to players breaking wood logs, but don't want to have a different event for each and every type of log, you can simply use
after player breaks *_log:.
So, you can make a script run when an event happens. You can also check the specific details of the event when it happens. But how do you do anything about the event? What, do we have to just
- narrate "*shakes fist* stop breaking my blocks!"?
determine command. The determine command is how you determine the outcome of an event. When not put to use, the event simply happens however it normally would. When you use determine, you can change anything about the event (within the reasonable limits of being an alteration to the event - if you want an unrelated change to the world to take place in response to the event, use the applicable command that produces that change).
The most commonly available determination, that almost all events support, is
- determine cancelled. This cancels the event - that is, the event will either not take place at all, or immediately undo itself. In the case of the
breaks block event, this will make it so the block never breaks (players watching will see nothing happen, however, the player that attempted to break a block will see it flash broken and then pop right back in - this is a consequence of clientside prediction and is unfortunately unavoidable without a client mod).
So, let's try it! After the narrate line in our test script, add the determine. If you've applied all the suggested changes thus far, your script should now look like this:
my_world_script: type: world events: after player breaks stone with:diamond_pickaxe: - narrate "Whoa <player.name>, you broke a <context.material.name>!" - determine cancelled
Wait... that didn't work. What went wrong?
This is where the difference between
on comes into play.
after means to run the script after the event happens and is already done.
on means to run the script before the event happens and can be changed. Effectively, when
after is used, the event is a past event, and when
on is used, the event is a future event. You can only use
after, for a pretty simple reason: You can't change the past!
So, change the
after in our example script to
my_world_script: type: world events: on player breaks stone with:diamond_pickaxe: - narrate "Whoa <player.name>, you broke a <context.material.name>!" - determine cancelled
This script theoretically means if you break a stone with a diamond pickaxe, you will be stopped. If you break a different block, or don't use a diamond pick, things will work normally.
Note that a determine command is considered by default the end of a script. if you want to use a determine command but then continue running commands in the script (for example, to apply multiple different determinations to change multiple parts of an event), you must use the
passively argument, like
- determine passively cancelled.
If you want to change an event, rather than simply cancelling it outright, most events have specific determination options available. The breaks block has the determination option
nothing to make no items drop when the block is broken in survival mode, and the option of inputting an item or list of items to make the block instead drop those listed items when broken.
As an example of how you might use this, you can use the tag
<MaterialTag.item> to get an item from any material, and combined with the
<context.material> context tag available, get the resulting line:
- determine <context.material.item>. You now have free silk touch powers! Every block now drops its exact raw form - so if you mine a diamond ore block for example, you'll receive the diamond ore block, instead of diamonds (note that you will have to change the event line back to
block instead of
stone, or specify
diamond_ore or anything like that).
As an additional note to be aware of: any one world script can contain several events. That might look something like this:
my_world_script: type: world events: after player breaks block: - narrate "Whoa <player.name>, you broke a <context.material.name>!" on player breaks stone with:diamond_pickaxe: - determine cancelled after player places stone: - narrate "Why are you putting that stone there? Better not break it with a diamond pickaxe!"