The event list
Lesson 10.3
This page lists every event the game sends today, with its fields. Come back to it whenever you write a new handler.
How to read the tables:
- Side says where the event happens. Server events run where the world is simulated: in singleplayer, or on a server that has your mod. Client events run in the game window of the player. Client and server explains the difference.
- Cancel marks events you can stop with
event:cancel(). - Fields in bold can be changed.
Startup and shutdown
These run once, in this order, while the game starts or a world opens.
| Event | Side | Use it for |
|---|---|---|
lifecycle.register |
both | Registering items and blocks. |
lifecycle.registries_frozen |
both | After this, nothing more can be registered. |
lifecycle.common_setup |
both | Setup that needs the registered content. |
lifecycle.client_setup |
client | Keybinds, HUD layers and other client things. |
lifecycle.server_setup |
server | Server setup. |
lifecycle.server_starting |
server | A world is about to open. |
lifecycle.server_started |
server | The world is open. |
lifecycle.server_stopping |
server | The world is closing. Save your data here. |
lifecycle.server_stopped |
server | The world is closed. |
lifecycle.shutdown |
both | The game is quitting. |
They have no fields you need.
A few more happen while the game runs:
| Event | Side | When |
|---|---|---|
lifecycle.level_load |
server | Once per world (overworld, nether, end) after it opened. world is that world. |
lifecycle.level_save |
server | Before a world is saved: autosave, /save-all, pausing singleplayer, closing. world is the world being saved. |
lifecycle.level_unload |
server | A world is about to close. |
lifecycle.data_reloaded |
server | After /reload. |
lifecycle.resources_reloaded |
client | After resource packs were reloaded (F3 + T, changing packs). |
lifecycle.mod_reload |
both | After a hot reload of a mod’s scripts worked (development mode). |
config.reloaded |
both | A config file was read again and a value changed (mod_id, file). Filter on your mod id, or use config:on_reload. |
When a hot reload adds a handler for lifecycle.register, common_setup, client_setup, server_starting or server_started again, the new handler runs straight away, so a reload behaves like a fresh start. |
Ticks
The game runs 20 ticks per second. These events run every tick, so keep their handlers short.
| Event | Side | Fields |
|---|---|---|
tick.server_pre |
server | tick_count: the number of the tick |
tick.server_post |
server | tick_count |
tick.level_pre |
server | world, tick_count: once per world, before the worlds tick |
tick.level_post |
server | world, tick_count: once per world, after they ticked |
tick.client_pre |
client | |
tick.client_post |
client |
pre runs before the game’s own work for that tick, post after it. When in doubt, use post.
Players
| Event | Side | Cancel | Fields |
|---|---|---|---|
player.join |
server | player |
|
player.leave |
server | player |
|
player.respawn |
server | player |
|
player.chat |
server | yes | player, message |
player.command |
server | yes | player, command (without the /) |
player.interact_block |
server | yes | player, world, hand, pos, face (result) |
player.interact_item |
server | yes | player, world, hand, stack, item (result) |
player.attack_entity |
server | yes | player, target |
Events marked result also take event:set_result(...), see Changing and cancelling. hand is "main" or "off". face is the side of the block that was clicked: "down", "up", "north", "south", "west" or "east". stack is the item stack in the hand and item its id.
Blocks
| Event | Side | Cancel | Fields |
|---|---|---|---|
block.break |
server | yes | player, world, pos, state, block, tool, item, xp, drop_items |
block.place |
server | yes | player, world, pos, state, block, hand, replaced_state |
state is the block state as a number and block the block’s id as a string. Most of the time you want block. On block.break, tool is a copy of the stack in the player’s hand and item its id (nil for an empty hand). xp is the experience the block drops (ores, sculk, spawners; 0 for most blocks), and you can change it. If you leave xp as it is, the game rolls the amount itself afterwards, exactly as without mods, so a mod that only reads it never changes what players get or the game’s random numbers. The value you read is a real roll of the same dice, but the game’s own roll may come out different.
Block events can be filtered by block id, entity events by entity type, player.interact_item by item id and player.interact_block by the clicked block. Changing and cancelling shows how.
Entities
| Event | Side | Cancel | Fields |
|---|---|---|---|
entity.damage |
server | yes | entity, player, world, source, attacker, amount |
entity.death |
server | yes | entity, player, world, source, attacker |
entity is the hurt or dying entity. player is the same entity when it is a player, otherwise nil. source is the damage type, like "minecraft:fall" or "minecraft:player_attack". attacker is whoever caused it, or nil. Cancelling entity.death skips the death. Give the entity some health back with entity:set_health(4) in the same handler, or it has none left.
Commands
| Event | Side | Fields |
|---|---|---|
server.command_register |
server | dedicated: true on a dedicated server |
Register your commands in here. Commands shows how.
Client
| Event | Side | Cancel | Fields |
|---|---|---|---|
client.connect |
client | address: the server address |
|
client.disconnect |
client | ||
client.screen_open |
client | screen_id |
|
client.key |
client | yes | key, scancode, action, modifiers, screen_open |
client.chat_send |
client | yes | message: what the player is about to send |
client.chat_received |
client | yes | message, overlay (true for action-bar text) |
client.sound_play |
client | yes | sound, category, pos, volume, pitch, positional |
Client events also fire on servers that don’t have your mod. That is what makes client-only mods possible.
More events are coming
The reference lists more events, such as entity ticks, explosions and block updates. They are not sent yet. Code that listens to them is harmless: the handler simply never runs.
Try it
Write one handler that greets a player when they join, and another that tells everybody in the log when a player died and what killed them.
Show a solution
cl.on("player.join", function(event)
event.player:send_message("Hi " .. event.player.name .. ", have fun!")
end)
cl.on("entity.death", function(event)
if not event.player then return end -- only players
local by = "something"
if event.attacker then by = event.attacker:type() end
cl.log("%s died (%s, by %s)", event.player.name, tostring(event.source), by)
end)
entity:type() gives an entity’s type id, like "minecraft:zombie".
Recap: startup events register and set up, tick events run 20 times a second, and player, block, entity and client events report what happens in the game.