Rules
Minecraft 26.1 and above
Unbreakables uses Shogi for rule syntax. Rules are configured in config/unbreakables-common.toml:
rules = [
"is_block('minecraft:spawner') -> refuse('You cannot break this block.')"
]
Rules are applied in order. If a rule refuses or fails, the block cannot be broken. If a rule adds a cost, such as xp or an item, the cost is only consumed when the block break succeeds.
Protecting blocks
To make a block unbreakable, refuse the break when that block is being mined:
rules = [
"is_block('minecraft:spawner') -> refuse('You cannot break this block.')"
]
Tags can be used instead of single blocks:
rules = [
"is_block('#minecraft:ores') -> refuse('You cannot break ores.')"
]
Creative players and players with creative-style instant build permissions bypass Unbreakables rules.
Break costs
Rules can require xp or items before a block can be broken:
rules = [
"is_block('minecraft:spawner') -> xp_level_cost(10)",
"is_block('minecraft:trial_spawner') -> item_cost('minecraft:nether_star', 1)"
]
Cooldowns can also be used to limit repeated breaks:
rules = [
"is_block('minecraft:spawner') -> cooldown_cost('spawner_break', '1h')"
]
When a rule blocks the break or requires something from the player, Unbreakables can show in-game feedback such as a message, remaining cooldown time, xp cost, item cost, or a combination of those.
Packaged rules
Mods and datapacks can provide named rules. Enable those rules with use('namespace:path').
For example, Waystones provides a rule for generated waystones:
rules = [
"use('waystones:generated_waystones')"
]
Player-placed blocks
Unbreakables adds an is_placed condition for rules that need to tell player-placed blocks apart from generated blocks. This is mainly useful for generated structure protection, where player-placed replacement blocks should stay breakable.
By default, placedBlockTracking is set to in_structure. In this mode, Unbreakables tracks eligible block placements inside generated structure pieces.
Blocks tagged as unbreakables:do_not_track and fluid states are not tracked. Changing this setting only affects placements observed after the setting is active; it does not retroactively classify blocks that already exist.
For the full list of generic Shogi conditions and effects, see the Shogi documentation.
Minecraft 1.20.1 and 1.21.1
Minecraft 1.20.1 and 1.21.1 use the legacy Unbreakables rule format. Custom rules are still configured in config/unbreakables-common.toml, but each rule uses this shape:
[condition1, condition2] requirement(args)
The condition list is optional. If a rule has multiple conditions, all of them must match before the requirement is applied. Rules are applied in order.
Protecting blocks
To make a block unbreakable, refuse the break when that block is being mined:
rules = [
"[is_block(minecraft:spawner)] refuse(You cannot break this block.)"
]
Tags can be used instead of single blocks:
rules = [
"[is_tag(minecraft:ores)] refuse(You cannot break ores.)"
]
Creative players and players with creative-style instant build permissions bypass Unbreakables rules.
Break costs
Rules can require xp or items before a block can be broken:
rules = [
"[is_block(minecraft:spawner)] add_level_cost(10)",
"[is_block(minecraft:trial_spawner)] add_item_cost(minecraft:nether_star, 1)"
]
Cooldowns can also be used to limit repeated breaks:
rules = [
"[is_block(minecraft:spawner)] add_cooldown(spawner_break, 3600)"
]
Cooldown durations are written as seconds in 1.20.1 and 1.21.1.
Packaged rulesets
Mods and datapacks can provide named rulesets. Enable those rulesets with the rulesets config option.
For example, Waystones provides a ruleset for generated waystones:
rulesets = [
"waystones:generated_waystones"
]
The packaged Waystones ruleset uses this legacy rule:
[is_tag(waystones:waystones), is_not_state(origin, player)] refuse($chat.waystones.cannot_break_waystone)
Legacy Conditions
| Condition | Description |
|---|---|
is_block(namespace:path) | Checks whether the mined block matches the given block id. |
is_block(#namespace:path) | Checks whether the mined block is in the given block tag. |
is_tag(namespace:path) | Checks whether the mined block is in the given block tag. |
is_state(property, value) | Checks a block state property. |
is_in_dimension(namespace:path) | Checks the dimension. |
is_in_biome(namespace:path) | Checks the biome id. |
is_in_biome(#namespace:path) | Checks the biome tag. |
has_effect(namespace:path, amplifier) | Checks whether the player has at least the given effect amplifier. |
is_tool(namespace:path) | Checks the held item id. |
is_tool(#namespace:path) | Checks the held item tag. |
is_enchanted(namespace:path, level) | Checks whether the held item has at least the given enchantment level. |
players_nearby(distance, count) | Checks nearby non-spectator players. |
mobs_nearby(distance, count) | Checks nearby mobs. |
animals_nearby(distance, count) | Checks nearby animals. |
entity_nearby(namespace:path, distance, count) | Checks nearby entities by entity type id. |
entity_nearby(#namespace:path, distance, count) | Checks nearby entities by entity type tag. |
is_above_y(y) | Checks whether the block is above a y level. |
is_below_y(y) | Checks whether the block is below a y level. |
is_within(minX, minY, minZ, maxX, maxY, maxZ) | Checks whether the block is inside a box. |
is_at(x, y, z) | Checks an exact position. |
is_near(x, y, z, distance) | Checks whether the block is near a position. |
is_near_poi(distance) | Checks for a nearby point of interest. |
has_advancement(namespace:path) | Checks whether the player has completed an advancement. Only available in 1.21.1. |
is_waystone_owner | Checks whether the player owns the Waystone at the mined position. Requires Waystones. |
is_waystone_global | Checks whether the Waystone at the mined position is global. Requires Waystones. |
Every condition also has a negated variant. For condition names starting with is_, insert not_ after is_, such as is_not_state(origin, player). For other conditions, prefix the name with not_, such as not_has_effect(minecraft:haste, 1).
Legacy Requirements
| Requirement | Description |
|---|---|
refuse(message) | Stops the break and shows the given message. Use $translation.key to show a translated message. |
add_level_cost(levels) | Adds an xp level cost. |
multiply_level_cost(multiplier) | Multiplies the xp level cost. |
scaled_add_level_cost(variable, scale) | Adds xp levels based on a context variable. |
scaled_multiply_level_cost(variable, scale) | Multiplies xp levels based on a context variable. |
min_level_cost(levels) | Enforces a minimum xp level cost. |
max_level_cost(levels) | Enforces a maximum xp level cost. |
add_xp_cost(points) | Adds an xp point cost. |
multiply_xp_cost(multiplier) | Multiplies the xp point cost. |
scaled_add_xp_cost(variable, scale) | Adds xp points based on a context variable. |
min_xp_cost(points) | Enforces a minimum xp point cost. |
max_xp_cost(points) | Enforces a maximum xp point cost. |
add_cooldown(id, seconds) | Adds a cooldown cost. |
multiply_cooldown(id, multiplier) | Multiplies a cooldown cost. |
scaled_add_cooldown(variable, id, seconds) | Adds a cooldown based on a context variable. |
scaled_multiply_cooldown(variable, id, seconds) | Multiplies a cooldown based on a context variable. |
min_cooldown(id, seconds) | Enforces a minimum cooldown. |
max_cooldown(id, seconds) | Enforces a maximum cooldown. |
add_item_cost(namespace:path, count) | Adds an item cost. |
multiply_item_cost(namespace:path, multiplier) | Multiplies an item cost. |
scaled_add_item_cost(variable, namespace:path, count) | Adds an item cost based on a context variable. |
scaled_multiply_item_cost(variable, namespace:path, count) | Multiplies an item cost based on a context variable. |
min_item_cost(namespace:path, count) | Enforces a minimum item cost. |
max_item_cost(namespace:path, count) | Enforces a maximum item cost. |