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.
| Subsystem | Point | Input | Output |
|---|---|---|---|
| World generation | worldgen.chunk | {dimension, x, z} (chunk coordinates) | {base, layers, edits} |
| Structures | structure.start | {dimension, set, x, z} | bool: the set starts a structure in that chunk |
| Player movement | move.player | {player, x, y, z, yaw, pitch, on_ground} | null |
| Block hardness | block.hardness | {block} | number: destroy time |
| Block breaking | block_break | see game events | |
| Loot and drops | loot.block | {block, at, tool?} | [[item, count], ...] |
loot.entity | {kind, entity, killed_by_player} | [[item, count], ...] | |
| Damage | damage | see 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) | |
| Projectiles | projectile.launch | {kind, world, pos: [x, y, z], shooter?: {id, kind, pos}, dispenser?: [x, y, z]} | bool: it is launched |
| Pistons | piston.move | {world, piston: [x, y, z], direction, extending, blocks: [[x, y, z], ...]} | bool: it moves |
| Fire | block.ignite | {world, pos: [x, y, z], fire, cause, player?} (fire is the block state written) | bool: the fire is lit |
| Spawning | spawn.mob | {kind, dimension, at, reason} | entity id, or null |
| AI goals | ai.goal | {entity, kind, position} | null |
| AI brains | ai.brain | {entity, kind, position} | null |
| Commands and chat | command.<root>, command_run, player_chat | see commands and game events | |
| Redstone | redstone.update | {dimension, at, source} | null |
| Fluids | fluid.tick | {dimension, at} | null |
| Natural block changes | block.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 |
| Portals | portal.travel | {player, dimension, to, turn?} | bool: the player moved |
| Permissions | permission.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 | |
| Network | packet.* | 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], withxandzfrom 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 (
nullfor 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:
| Cause | What changes |
|---|---|
crop_growth | a crop, a stem or nether wart ripening a stage (the random tick) |
vine_growth | a vine spreading, or a weeping or twisting vine growing (the random tick) |
farmland_dry | farmland with no water and nothing on it turning back to dirt |
snow_form | snow settling in the weather, a layer at a time |
ice_form | still water at the top of a column freezing in a cold biome's weather |
frosted_ice_melt | Frost Walker's ice ageing a stage, or melting to water |
sapling | a 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_decay | leaves with no log within six decaying, and dropping their loot |
grass_spread | grass spreading onto dirt (raw brightness 9 or more above it) |
mycelium_spread | mycelium spreading onto dirt |
grass_decay | grass or mycelium dying back to dirt under a block light cannot pass up into, or under a fluid |
mushroom_spread | a mushroom spreading to a block nearby, while no more than five of its kind stand about it |
snow_melt | a snow layer melting in block light above 11, dropping its snowball |
ice_melt | ice melting in block light above 10: to water, or to nothing where water evaporates (the Nether) |
coral_fade | a 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
falseand the block stays as it was;trueand 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.
kindis the entity type (minecraft:villager);posis where it stands.attackeris who the blow is blamed on (the archer);directis what struck (the arrow), the attacker itself for a swing, andnullfor a fall or a fire.launched_atis where the shot that struck was fired from, for a projectile's blow (its arrow, its blast): judge a shot by where it left.nullotherwise. A summoned projectile's is where it was summoned.
Nothing is built when no plugin hooks it.
- prepend: change
amount, orcause(the damage type the game then uses). The entities are only what is told. - override: answer the amount that lands.
0and 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.
-
shooteris whoever fired it (nullfor a dispenser or a command). -
dispenseris 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
falseand 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
falseand 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:
| Cause | What lights it |
|---|---|
lava | lava's random tick lighting a block near it |
lightning | a lightning bolt's fires |
fire_charge | a small fireball landing (a blaze's, a dispenser's), or a fire charge used on a block (player is set) |
flint_and_steel | flint and steel, a player's (player is set) or a dispenser's |
fire_spread | a fire spreading to a block, or burning one away into more fire |
explosion | the 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
falseand the block stays as it was;trueand 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
/executeis 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
/helpand their Bedrock command list follow the same answer. - Whole commands only. Granting
gamemodegrants/gamemode creative <another player>too; grantingopamounts 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.