Hook points
Game events as hook points
Each event is also a hook point with the same name. You can still veto the step it reports, as before, but you can now also run before it, run instead of it, or change what came of it.
For each point the input is a record. Positions are [x, y, z]. A prepend's replacement is
the same record with changed fields.
| Point | Input | Output | prepend | override | append |
|---|---|---|---|---|---|
block_break | {player, at, block} (block is what the edit writes: air) | bool: the break may go ahead | move the break, or write something other than air | refuse (false) or allow it past the event | refuse |
block_place | {player, at, block} | bool | move it, or change the block (by name; default state) | refuse or allow | refuse |
player_interact | {player, at} (the block clicked) | bool | move the click (any placement beside it moves too) | refuse or allow | refuse |
damage | {victim, attacker?, cause, amount} | {damage, raw, resisted, absorbed, health, died} | change the blow | answer what it did; nothing is taken off | change what the caller (knockback, thorns, statistics) sees |
death | {victim, killer?, message} | {keep_inventory, keep_experience} (both the keep_inventory rule's answer), or null | report a different death | report it yourself; no death screen or message, and null means no drops either, while {keep_inventory, keep_experience} has the game drop what it does not keep | runs after the death screen and message, before the drops: say what this one death keeps (null drops and does nothing more) |
game_mode_changed | {player, mode} (0 survival, 1 creative, 2 adventure, 3 spectator) | bool changed | change the mode | answer; nothing changes | change the answer /gamemode reports |
player_chat | {player, message} | {relay, message} | change the words (they go out unsigned, "Not Secure", as the game sends any unsigned line) | decide whether and what to relay | change that decision |
command_run | {player?, command, rest} | bool runs | change the command (a new name is checked for permission again) | decide whether it runs | change that |
player_joined | {player, name} | string?: the line everybody is told | change the name | answer the line (null: none); the event is not fired | change the line |
player_left | {player, name} | string? | change the name | answer the line; the event is not fired; the player is removed regardless | change the line |
player_payload | {player, channel, data} (data as hex) | null | change the channel or the bytes | handle it yourself (the brand is not recorded, the event not fired) | runs after |
player_move_block | {player, from, to} | null | change what listeners are told | tell nobody, or tell them yourself | runs after |
tick | {tick} | null | change the count | skip the tick for every listening plugin, the game's own rules included | runs after |
server_started | null | null | runs before any plugin's start | start nothing, tell nobody | runs after |
server_stopping | null | null | runs while everything is loaded and unsaved | replace the telling, stopping and save | runs after the save |
inventory_click | {player, window, slot, button, mode} (the wire's) | bool goes ahead | change the click | answer whether it goes ahead | change that |
player_edit | {player, world, at, action} | bool goes ahead | change what is told | answer whether it goes ahead | change that |
entity_harm | {player, target, kind, world, at, cause} | bool goes ahead | change what is told | answer whether it goes ahead | change that |
player_swing | {player, hand} | bool goes ahead | swing the other hand | answer whether it goes ahead | change that |
player_use_item | {player, hand} | bool | answer the use from the other hand | answer whether it goes ahead | change that |
player_swap_hands | {player, slot} (the hotbar slot, 0-8, that changes places with the off hand: the selected one) | bool | swap another hotbar slot | answer whether it goes ahead | change that |
player_drop_item | {player, whole_stack} | bool | throw the whole stack, or one | answer whether it goes ahead | change that |
player_hotbar_change | {player, from, to} (hotbar slots, 0-8) | bool | select another slot | answer whether it goes ahead | change that |
item_crafted | {player, item, count, recipe} | null | change what is told | tell nobody | run after it is told |
fish_caught | {player, item, table} | string: the item that comes up, empty for none | change the catch | answer the catch ("" for nothing) | change the answered catch |
loot_generated | {source, table, player?, entity?, items} (items is [[item, count], ...]) | [[item, count], ...]: the stacks placed | add or remove stacks | answer the stacks | change the answered stacks |
advancement_done | {player, advancement} | null | change what is told | tell nobody | run after it is told |
statistic_changed | {player, stat, key, value, amount} | null | change what is told | tell nobody | run after it is told |
player_resource_pack | {player, pack, status} | null | change what is told | tell nobody (the game still acts on the answer) | run after it is told |
player_respawn | {player, world, at} | null | change what is told | tell nobody | run after it is told |
player_pickup_item | {player, entity} (the item entity) | bool: it is picked up | change what is told (the pickup is the one the game found) | answer whether it goes ahead | change that |
block_hit | {player, world, at, face} (face the wire's number, 0 down to 5 east) | bool: the dig goes ahead | change what is told (the dig is the packet's) | answer whether it goes ahead | change that |
entity_attack | {player, target} | bool: the blow goes ahead | strike another entity | answer whether it goes ahead | change that |
reserved_exploded | {world, at, blocks} (blocks is [[x, y, z], ...]) | null | change what is told | tell nobody | run after it is told |
ores_exploded | {world, at, player, nearby, blocks} (nearby is [[player, distance], ...], blocks is [[at, block], ...]) | null | change what is told | tell nobody | run after it is told |
entity_mounted | {rider, vehicle} | bool: it goes ahead | change what is told | answer whether it goes ahead | change that |
entity_dismounted | {rider, vehicle} | bool: it goes ahead, where it can be refused | change what is told | answer whether it goes ahead | change that |
inspect_change | {player, name, view, changes} (changes is [[item, count], ...]; the account is not offered) | null | change what is told | tell nobody | run after it is told |
blocks_pushed | {world, moved} (moved is [[from, to, block], ...]) | null | change what is told | tell nobody | run after it is told |
entity_interact | {player, target, hand} (hand is the wire's: 0 main, 1 off) | bool: the click goes ahead | point the click at another entity; the game then acts on that one | answer whether it goes ahead; the event is not fired | change that |
container_change | {player, world, at, block, changes} (changes is [[item, count], ...]) | null | change what is told | tell nobody | run after it is told |
mob_death | {victim, kind, killer?} | null | change what is told (the event's world, at and cause are the creature's own, not the input's) | tell nobody; the creature dies either way | run after it is told |
world_changed | {player, from, to} | null | change what is told | tell nobody; the player has moved either way | run after it is told |
settings_changed | {plugin, key, value} (value is the new value as JSON text) | string: why the change was refused; empty when it was kept | change whose setting, which key, or the value | answer the refusal; nothing is kept | change the answer |
network_message | {from, channel, payload} (payload as hex) | null | change where it is from, its channel (and so who hears it) or its bytes | handle it yourself; no subscriber is told | runs after they are told |
settings_changed runs once for each key set, through the portal or setting_set; the event that
follows names every key that changed.
Six events have no hook point: player_look, player_input, creature_order_done,
creature_sees, creature_damaged and creature_gone. They are told only to the plugin that asked
for them (watch_look, watch_input) or that owns the creature, not through the bus.
Related points
move.player: every position a client reports, before the server judges it. See subsystems.- Fall damage goes through
damagetoo, withcause: "minecraft:fall". entity.damage: any living thing's blow, a creature's as well as a player's, asked beforedamage. See subsystems.death'skeep_inventorykeeps every item where it is and drops none;keep_experiencedrops no orbs and carries the bar through the respawn. Each is for that one death, as the rule would.
From docs/api/reference/hooks/game-events.md in the RustMC repository at bbd81209ad.