Hook points

Subsystem hook points

On this page

Each subsystem below has a hook point at its choke point: the one function every path to that step goes through.

SubsystemPointInputOutput
World generationworldgen.chunk{dimension, x, z} (chunk coordinates){base, layers, edits}
Structuresstructure.start{dimension, set, x, z}bool: the set starts a structure in that chunk
Player movementmove.player{player, x, y, z, yaw, pitch, on_ground}null
Block hardnessblock.hardness{block}number: destroy time
Block breakingblock_breaksee game events
Loot and dropsloot.block{block, at, tool?}[[item, count], ...]
loot.entity{kind, entity, killed_by_player}[[item, count], ...]
Damagedamagesee game events
entity.damage{victim: {id, kind, pos}, world, cause, amount, attacker?: {id, kind, pos}, direct?: {id, kind, pos}, launched_at?: [x, y, z]}number: the amount that lands (0: none)
Projectilesprojectile.launch{kind, world, pos: [x, y, z], shooter?: {id, kind, pos}, dispenser?: [x, y, z]}bool: it is launched
Pistonspiston.move{world, piston: [x, y, z], direction, extending, blocks: [[x, y, z], ...]}bool: it moves
Fireblock.ignite{world, pos: [x, y, z], fire, cause, player?} (fire is the block state written)bool: the fire is lit
Spawningspawn.mob{kind, dimension, at, reason}entity id, or null
AI goalsai.goal{entity, kind, position}null
AI brainsai.brain{entity, kind, position}null
Commands and chatcommand.<root>, command_run, player_chatsee commands and game events
Redstoneredstone.update{dimension, at, source}null
Fluidsfluid.tick{dimension, at}null
Natural block changesblock.natural_change{world, pos: [x, y, z], from, to, cause} (from and to are block states, e.g. minecraft:wheat[age=3])bool: the change goes ahead
Portalsportal.travel{player, dimension, to, turn?}bool: the player moved
Permissionspermission.commands{player, level, commands: [{command, needed, node}]} (node is the node a plugin's manifest names for its command; empty for none)the names they may use
permission.node{name, uuid, node, level, needed}bool: they hold the node
Networkpacket.*see packets

worldgen.chunk

This runs each time a chunk is generated for the first time. Chunks already saved are read from disk and never pass through it.

  • prepend: generate the terrain of a different chunk here. Chests, spawners and creatures that chunk would carry are left out, because they are placed by their own coordinates.
  • override: answer the chunk yourself, and the game's generator does not run: {"base": "layers", "layers": [[y, "minecraft:stone"], ...], "edits": []}. Every height not listed is air.
  • append: lay edits ([x, y, z, block], with x and z from 0 to 15 within the chunk) over whatever was generated. Keep "base": "vanilla" to keep the game's terrain.

structure.start

This decides whether a structure set (minecraft:villages, minecraft:mineshafts, ...) starts in a chunk. Generation and every search for a structure (/locate, explorer maps, dolphins) consult the same answer, so they never disagree.

  • prepend: ask the game about another chunk instead.
  • override: answer.
  • append: change the answer.

move.player

  • prepend: change the pose the server goes on to judge. It is still checked like anything a client sent.
  • override: take the packet. It is neither checked nor applied.
  • append: runs once the move has been judged and applied.

block.hardness

This is the destroy time, where a negative value means unbreakable and 0 means it breaks at a touch. The server reads it to decide whether a block may be broken at all, and whether a survival player's first hit breaks it. Mining speed is timed by the client from its own registry, and no hook reaches that.

loot.block, loot.entity

These are what a broken block, or a creature's death, drops.

  • prepend: roll a different block's or kind's table.
  • override: answer the drops, and the table is not rolled.
  • append: change the drops.

A stack the hooks left exactly as the table rolled it keeps its components (enchantments, potions). A changed stack is a plain one.

spawn.mob

Every creature placed in the world passes through this: summoned, bred, hatched, spawned naturally or by a spawner or structure.

  • prepend: change what spawns and where.
  • override: answer the id of what you placed (null for nothing), and the game places nothing.
  • append: change the id the caller acts on.

ai.goal, ai.brain

This is one step of a creature's mind. ai.brain is for creatures with a brain (villagers, piglins, hoglins, axolotls, frogs, ...). ai.goal is for creatures run by a goal selector.

  • prepend: move the creature before it thinks.
  • override: think in its place. It does nothing of its own that step.
  • append: runs after.

redstone.update

A block is told a neighbour changed. This covers the redstone components and everything else that answers to power: doors, pistons, dispensers, note blocks, rails and command blocks.

  • prepend: send it to another position.
  • override: answer in the block's place, and it does not react.
  • append: runs after.

fluid.tick

This is one scheduled tick of water or lava: falling, spreading, drying up, or lava turning to stone.

  • prepend: tick another position.
  • override: the fluid does not move.
  • append: runs after.

block.natural_change

A block the world changes by itself, asked just before it is written. cause is one of:

CauseWhat changes
crop_growtha crop, a stem or nether wart ripening a stage (the random tick)
vine_growtha vine spreading, or a weeping or twisting vine growing (the random tick)
farmland_dryfarmland with no water and nothing on it turning back to dirt
snow_formsnow settling in the weather, a layer at a time
ice_formstill water at the top of a column freezing in a cold biome's weather
frosted_ice_meltFrost Walker's ice ageing a stage, or melting to water
saplinga sapling advancing a stage or growing into its tree (one ask for the whole tree, at the sapling: to is what the tree puts there), a hanging mangrove propagule ripening, or a bamboo sapling putting up its first stalk. Bone meal is not natural and is not asked
leaf_decayleaves with no log within six decaying, and dropping their loot
grass_spreadgrass spreading onto dirt (raw brightness 9 or more above it)
mycelium_spreadmycelium spreading onto dirt
grass_decaygrass or mycelium dying back to dirt under a block light cannot pass up into, or under a fluid
mushroom_spreada mushroom spreading to a block nearby, while no more than five of its kind stand about it
snow_melta snow layer melting in block light above 11, dropping its snowball
ice_meltice melting in block light above 10: to water, or to nothing where water evaporates (the Nether)
coral_fadea coral block, plant or fan with no water about it dying, 60 to 99 ticks after it was left dry

A change a player or a creature makes (bone meal, a bee, trampling) is not natural and does not pass here. Nothing is built when no plugin hooks it.

  • prepend: change to, the state written in its place. A state this server does not know is refused and the original is written. The other fields are only what is told.
  • override: answer false and the block stays as it was; true and it changes.
  • append: change that answer.

entity.damage

Any living thing about to take damage, from any cause: a player, a creature, an armour stand. Mob on mob, mob on villager, a fall, fire, lava, drowning, a blast, a shot from a mob or a dispenser, a command. Asked at each of the three doors a blow comes through: a player's hurt (before the player-only damage point), a creature's hurt, and a player's swing at a creature.

  • kind is the entity type (minecraft:villager); pos is where it stands.
  • attacker is who the blow is blamed on (the archer); direct is what struck (the arrow), the attacker itself for a swing, and null for a fall or a fire.
  • launched_at is where the shot that struck was fired from, for a projectile's blow (its arrow, its blast): judge a shot by where it left. null otherwise. A summoned projectile's is where it was summoned.

Nothing is built when no plugin hooks it.

  • prepend: change amount, or cause (the damage type the game then uses). The entities are only what is told.
  • override: answer the amount that lands. 0 and nothing happens: no damage, no knockback, no grudge.
  • append: change that amount.

projectile.launch

A projectile or fireball about to be created: an arrow, a trident, a snowball, an egg, a pearl, a potion, a rocket, a ghast's fireball, a blaze's or a dispenser's fire charge, a wither skull, a shulker bullet, an eye of ender, a wind charge, a summoned one. Fired by a player, a creature or a dispenser.

  • shooter is whoever fired it (null for a dispenser or a command).

  • dispenser is the block it came out of, for a dispensed one.

  • prepend: change pos, where it starts. The other fields are only what is told.

  • override: answer false and nothing is created (a dispenser fails, keeping its item).

  • append: change that answer.

piston.move

A piston about to extend or retract: once per move, before any block moves, never per block. direction is the way it faces (north, up, ...). blocks is every block it would push, pull (a sticky piston's retract) or destroy; a plain piston's retract takes none but its own head. Nothing is built when no plugin hooks it. A guarded zone line still stops a push on its own.

  • prepend: the input is only what is told.
  • override: answer false and the piston stays as it was (extended or not) and no block moves.
  • append: change that answer.

block.ignite

A fire about to be lit. cause is one of:

CauseWhat lights it
lavalava's random tick lighting a block near it
lightninga lightning bolt's fires
fire_chargea small fireball landing (a blaze's, a dispenser's), or a fire charge used on a block (player is set)
flint_and_steelflint and steel, a player's (player is set) or a dispenser's
fire_spreada fire spreading to a block, or burning one away into more fire
explosionthe fire a fiery blast leaves (a ghast's fireball, a bed or a respawn anchor)

A player's flint and steel still asks player_edit with ignite first. Nothing is built when no plugin hooks it.

  • prepend: change fire, the state written (minecraft:soul_fire); a state this server does not know is refused and the original is written. The other fields are only what is told.
  • override: answer false and the block stays as it was; true and it is lit.
  • append: change that answer.

portal.travel

Every change of dimension passes through this: a portal, an End exit, /execute in … run tp, or a respawn elsewhere.

  • prepend: change the destination.
  • override: answer whether the move happened, and nobody moves.
  • append: change that answer. A portal crossing plays its sound only on true.

permission.commands

Decides which commands a player may use -- built-in and plugin commands alike, by root (gamemode, tp, a plugin's own) -- for running one and for the command list they are sent. It is asked with a batch: building a list is one call with every root, running a command is one call with that root alone. The game's rule keeps each command whose needed <= level; the output is the names kept. This is the hook a permissions plugin overrides to answer from its own nodes.

  • prepend: change the level they count as, or which commands are asked about.
  • override: answer the names yourself; the operator level is not consulted.
  • append: add names to, or take them from, the game's answer.

What it covers, and what it does not:

  • A command under /execute is asked about for the player who typed the /execute, at the /execute's level: a command denied to them stays denied inside it.
  • A player's /help and their Bedrock command list follow the same answer.
  • Whole commands only. Granting gamemode grants /gamemode creative <another player> too; granting op amounts to granting owner. Selectors (@a) still need operator level two.
  • Bare /op, which only lists the operators, is never asked about. Neither is a command needing more than level four: nobody may run those.
  • The console and command blocks run at their own levels, as before. A function a player runs is asked about line by line, for that player.

With no plugin hooked, nothing is asked and every list is the operator level's, byte for byte. After changing what someone may use, call refresh_commands(player): the list is sent at the next tick, decided afresh -- not during the call, since the plugin calling is usually the one hooked here, and still busy.

permission.node

Whether a player holds one permission node. The server asks this where it needs a node itself: the web portal (web_portal.rs), maintenance mode's bypass (maintenance.rs) and backups (backups.rs). The player may be offline. name is their name, uuid their account id in hex (empty when unknown), level their operator level, and needed the level that holds the node by default. The game's answer is level >= needed.

  • prepend: change who is asked about, the node, or the levels.
  • override: answer from your own nodes; the operator level is not consulted.
  • append: change the answer.

Maintenance mode asks on threads of its own and waits only a short time for an answer: a player not answered in time is decided by their operator level.

From docs/api/reference/hooks/subsystems.md in the RustMC repository at bbd81209ad.