Events and listeners
React to things that happen in the game: joins, block breaks, damage, chat and hundreds more.
Almost every plugin reacts to something players do. On this page you will write a listener that greets players when they join, stop players from mining diamond ore, change death messages, and learn how to find the right event for any idea.
What is an event?
A Minecraft server is busy. Every second, players join, walk, break blocks, open chests, chat and fight. Each time one of these things happens, Paper creates an event: a small Java object that describes what is happening right now. Who joined? Which block is breaking? How much damage is the zombie about to take?
Before Paper finishes the action, it hands that event to every plugin that asked to hear about it. Your plugin can read the details, change some of them, or even stop the action completely.
Three words come up again and again on this page:
- Event: the thing that happened, as an object you can ask questions, such as
PlayerJoinEventorBlockBreakEvent. - Listener: a class you write that wants to hear about events.
- Event handler: a method inside your listener that Paper calls when the event happens.
Your first listener
Here is the whole plan for a plugin that says hello to every player who joins. It needs exactly three pieces:
Write a class that implements Listener
This tells Paper "this class contains event handlers".
Listeneris an interface with no methods in it; it works like a label you stick on the class.Add a method marked with @EventHandler
The method takes one parameter: the event you care about. Paper looks at that parameter's type to decide when to call the method.
Register the listener when the plugin starts
In your main class's
onEnablemethod, hand an object of your listener class to Paper's plugin manager. Until you do this, Paper does not know your listener exists.
This is the listener. Read the notes under the code, then hover any word in the code to see what it is.
Where this file livesevents-first-listenersrcmainjavacomexampleeventsfirstlistenerWelcomeListener.java
The package com.example.eventsfirstlistener is the folder path com/example/eventsfirstlistener inside src/main/java: every dot in the package name is one folder. IntelliJ creates these folders for you when you make a new package.
- events-first-listener/
- src/main/
- java/com/example/eventsfirstlistener/Package com.example.eventsfirstlistener
- FirstListenerPlugin.javaMain class: Paper starts here (named as main in plugin.yml)
- WelcomeListener.javayou are hereListener: reacts to events
- resources/Files copied into the jar as they are
- plugin.ymlTells Paper the plugin's name, version and main class
- java/com/example/eventsfirstlistener/Package com.example.eventsfirstlistener
- build.gradle.ktsThe build recipe: Paper 26.3 API, Java 25, how the jar is made
- gradle.propertiesVersion numbers used by the build
- gradlew.batRuns Gradle on Windows without installing it
- settings.gradle.ktsThe project's name
- src/main/
Gray files come with the project template; you rarely edit them. Open the whole project in the Compile Lab, or download it from the project page.
1package com.example.eventsfirstlistener;2 3import org.bukkit.entity.Player;4import org.bukkit.event.EventHandler;5import org.bukkit.event.Listener;6import org.bukkit.event.player.PlayerJoinEvent;7 8public final class WelcomeListener implements Listener {9 10 @EventHandler11 public void onJoin(PlayerJoinEvent event) {12 Player player = event.getPlayer();13 player.sendRichMessage("<green>Welcome to the server!");14 }15}- The
package is the folder path of this file. It must match the folders undersrc/main/java. - Imports tell Java which classes you mean. IntelliJ adds them for you when you press Alt+Enter on a red name.
- This class promises to be a listener. Without
implements Listener, Paper refuses to register it. - An
annotation . It marks the next method as an event handler. Paper ignores methods without it. - The method name can be anything you like;
onJoinjust describes it. What matters is the parameter type,PlayerJoinEvent, which means "call me whenever a player joins". - Every player event knows which player it is about. This saves that player in a variable called
player. - Sends a chat message to this one player. The text uses MiniMessage tags, so
<green>makes it green.
And this is the plugin's main class, which registers the listener when the plugin is enabled:
Where this file livesevents-first-listenersrcmainjavacomexampleeventsfirstlistenerFirstListenerPlugin.java
The package com.example.eventsfirstlistener is the folder path com/example/eventsfirstlistener inside src/main/java: every dot in the package name is one folder. IntelliJ creates these folders for you when you make a new package.
- events-first-listener/
- src/main/
- java/com/example/eventsfirstlistener/Package com.example.eventsfirstlistener
- FirstListenerPlugin.javayou are hereMain class: Paper starts here (named as main in plugin.yml)
- WelcomeListener.javaListener: reacts to events
- resources/Files copied into the jar as they are
- plugin.ymlTells Paper the plugin's name, version and main class
- java/com/example/eventsfirstlistener/Package com.example.eventsfirstlistener
- build.gradle.ktsThe build recipe: Paper 26.3 API, Java 25, how the jar is made
- gradle.propertiesVersion numbers used by the build
- gradlew.batRuns Gradle on Windows without installing it
- settings.gradle.ktsThe project's name
- src/main/
Gray files come with the project template; you rarely edit them. Open the whole project in the Compile Lab, or download it from the project page.
1package com.example.eventsfirstlistener;2 3import org.bukkit.plugin.java.JavaPlugin;4 5public final class FirstListenerPlugin extends JavaPlugin {6 7 @Override8 public void onEnable() {9 getServer().getPluginManager().registerEvents(new WelcomeListener(), this);10 }11}- Your main class. Paper creates it when the server starts, because
plugin.ymlnames it asmain. - Paper calls
onEnableonce, when your plugin starts. This is where you set everything up. - Registers the listener. The first argument is a new
WelcomeListenerobject; the second,this, is your plugin, so Paper knows who owns the listener.
Build the plugin, start your test server with Run Paper Server (see Your first plugin), and join with localhost. As soon as you arrive, you see this in chat:
Welcome to the server!
The yellow line is Minecraft's normal join message, which goes to everyone. The green line is yours, and only the player who joined sees it, because you sent it to player.
How Paper finds your handler
When your plugin calls registerEvents, Paper looks through your listener class for methods to call later. A method becomes an event handler when all of these are true:
- It has the
@EventHandlerannotation directly above it. - It has exactly one parameter.
- That parameter's type is an event class, such as
PlayerJoinEvent.
Handlers are public by convention. Paper also accepts private methods, but every example you will read online uses public, so stick with that. The return type is always void, because Paper does nothing with a returned value.
If a handler has the wrong shape, for example a parameter that is a Player instead of an event, Paper skips it and prints an error when your plugin starts:
[14:02:11 INFO]: [FirstListener] Enabling FirstListener v1.0.0[14:02:11 ERROR]: [FirstListener] FirstListener v1.0.0 attempted to register an invalid EventHandler method signature "public void com.example.eventsfirstlistener.WelcomeListener.onJoin(org.bukkit.entity.Player)" in class com.example.eventsfirstlistener.WelcomeListenerGetting information from the event
An event object is full of useful methods. A BlockBreakEvent knows the block and the player; an EntityDamageEvent knows the entity, the damage amount and the cause. The fastest way to explore is IntelliJ's autocomplete: type event. inside your handler and look at the list.
These are the events beginners use most. Each one lives in a package you need to import; IntelliJ does that for you.
| Event | Fires when | Can you cancel it? |
|---|---|---|
PlayerJoinEvent | A player finishes joining the server. | No |
PlayerQuitEvent | A player leaves the server. | No |
AsyncChatEvent | A player sends a chat message (runs off the main thread). | Yes |
PlayerInteractEvent | A player left-clicks or right-clicks the air or a block, or steps on a pressure plate. | Yes |
PlayerMoveEvent | A player moves or turns their head. It fires very often. | Yes |
BlockBreakEvent | A player breaks a block. | Yes |
BlockPlaceEvent | A player places a block. | Yes |
EntityDamageEvent | Any entity, including a player, is about to take damage. | Yes |
PlayerDeathEvent | A player dies. | Yes |
InventoryClickEvent | A player clicks a slot in any open inventory. | Yes |
PlayerDropItemEvent | A player drops an item. | Yes |
Paper 26.3 has 465 event classes. You do not need to memorize them: the Event Finder lets you search them in plain English ("when a player opens a chest") and gives you listener code to copy.
A bigger example: join and quit messages
Events do more than tell you what happened. Many of them let you change the outcome. PlayerJoinEvent lets you replace the yellow "joined the game" message that everyone sees, and PlayerQuitEvent does the same for leaving.
Where this file livesevents-demosrcmainjavacomexampleeventsdemoJoinQuitListener.java
The package com.example.eventsdemo is the folder path com/example/eventsdemo inside src/main/java: every dot in the package name is one folder. IntelliJ creates these folders for you when you make a new package.
- events-demo/
- src/main/
- java/com/example/eventsdemo/Package com.example.eventsdemo
- DeathListener.javaListener: reacts to events
- EventsDemoPlugin.javaMain class: Paper starts here (named as main in plugin.yml)
- JoinQuitListener.javayou are hereListener: reacts to events
- ProtectionListener.javaListener: reacts to events
- resources/Files copied into the jar as they are
- plugin.ymlTells Paper the plugin's name, version and main class
- java/com/example/eventsdemo/Package com.example.eventsdemo
- build.gradle.ktsThe build recipe: Paper 26.3 API, Java 25, how the jar is made
- gradle.propertiesVersion numbers used by the build
- gradlew.batRuns Gradle on Windows without installing it
- settings.gradle.ktsThe project's name
- src/main/
Gray files come with the project template; you rarely edit them. Open the whole project in the Compile Lab, or download it from the project page.
1package com.example.eventsdemo;2 3import java.time.Duration;4import net.kyori.adventure.text.minimessage.MiniMessage;5import net.kyori.adventure.text.minimessage.tag.resolver.Placeholder;6import net.kyori.adventure.title.Title;7import org.bukkit.entity.Player;8import org.bukkit.event.EventHandler;9import org.bukkit.event.Listener;10import org.bukkit.event.player.PlayerJoinEvent;11import org.bukkit.event.player.PlayerQuitEvent;12 13public final class JoinQuitListener implements Listener {14 15 private static final MiniMessage MINI_MESSAGE = MiniMessage.miniMessage();16 17 @EventHandler18 public void onJoin(PlayerJoinEvent event) {19 Player player = event.getPlayer();20 event.joinMessage(MINI_MESSAGE.deserialize("<gray>[<green>+</green>] <name>",21 Placeholder.unparsed("name", player.getName())));22 player.showTitle(Title.title(23 MINI_MESSAGE.deserialize("<gold>Welcome!"),24 MINI_MESSAGE.deserialize("<gray>Have fun, <name>", Placeholder.unparsed("name", player.getName())),25 Title.Times.times(Duration.ofMillis(500), Duration.ofSeconds(3), Duration.ofMillis(500))));26 }27 28 @EventHandler29 public void onQuit(PlayerQuitEvent event) {30 event.quitMessage(MINI_MESSAGE.deserialize("<gray>[<red>-</red>] <name>",31 Placeholder.unparsed("name", event.getPlayer().getName())));32 }33}- One shared MiniMessage object turns tag text such as
<gray>into colored chat components.static finalmeans there is exactly one, and it never changes. - Replaces the join message that every online player sees. The method takes a
Component , which is Paper's type for formatted text. - Fills the
<name>tag with the player's name. "Unparsed" means the name is inserted as plain text, so it can never inject formatting tags. - Shows a big title in the middle of this player's screen, with a smaller subtitle underneath.
- How long the title fades in, stays, and fades out.
- A second handler in the same class. One listener can hold as many handlers as you like.
Here is what everyone sees when Steve joins, and what Steve sees on his screen:
Canceling events
Some events can be stopped. They implement an interface called Cancellable (the API spells it with two l's), and they have a method setCancelled(true). When a listener cancels an event, Paper does not finish the action: the block does not break, the damage is not dealt, the message is not sent.
This listener protects diamond ore. Players without a special permission cannot mine it:
Where this file livesevents-demosrcmainjavacomexampleeventsdemoProtectionListener.java
The package com.example.eventsdemo is the folder path com/example/eventsdemo inside src/main/java: every dot in the package name is one folder. IntelliJ creates these folders for you when you make a new package.
- events-demo/
- src/main/
- java/com/example/eventsdemo/Package com.example.eventsdemo
- DeathListener.javaListener: reacts to events
- EventsDemoPlugin.javaMain class: Paper starts here (named as main in plugin.yml)
- JoinQuitListener.javaListener: reacts to events
- ProtectionListener.javayou are hereListener: reacts to events
- resources/Files copied into the jar as they are
- plugin.ymlTells Paper the plugin's name, version and main class
- java/com/example/eventsdemo/Package com.example.eventsdemo
- build.gradle.ktsThe build recipe: Paper 26.3 API, Java 25, how the jar is made
- gradle.propertiesVersion numbers used by the build
- gradlew.batRuns Gradle on Windows without installing it
- settings.gradle.ktsThe project's name
- src/main/
Gray files come with the project template; you rarely edit them. Open the whole project in the Compile Lab, or download it from the project page.
1package com.example.eventsdemo;2 3import org.bukkit.Material;4import org.bukkit.block.Block;5import org.bukkit.entity.Player;6import org.bukkit.event.EventHandler;7import org.bukkit.event.Listener;8import org.bukkit.event.block.BlockBreakEvent;9 10public final class ProtectionListener implements Listener {11 12 @EventHandler13 public void onBreak(BlockBreakEvent event) {14 Block block = event.getBlock();15 Player player = event.getPlayer();16 boolean isDiamondOre = block.getType() == Material.DIAMOND_ORE17 || block.getType() == Material.DEEPSLATE_DIAMOND_ORE;18 if (!isDiamondOre) {19 return;20 }21 if (player.hasPermission("eventsdemo.mine.diamonds")) {22 return;23 }24 event.setCancelled(true);25 player.sendRichMessage("<red>You need permission to mine diamond ore here.");26 }27}- Called every time any player breaks any block.
- Checks which block it is.
Materiallists every block and item type. Diamond ore has two versions: normal stone ore and deepslate ore, so the code checks both. - If the block is not diamond ore, this handler has nothing to do, so it stops right away with
return. This style is called aguard clause . - Players with the permission are allowed through. Server owners give permissions with a permissions plugin such as LuckPerms.
- Cancels the event. The ore stays exactly where it was, and nothing drops.
- Always tell players why something did not work, or they will think the server is broken.
The permission is declared in plugin.yml, so Paper knows who has it by default:
Where this file livesevents-demosrcmainresourcesplugin.yml
Files in src/main/resources are copied into the jar exactly as they are. Paper looks for plugin.yml at the top of the jar.
- events-demo/
- src/main/
- java/com/example/eventsdemo/Package com.example.eventsdemo
- DeathListener.javaListener: reacts to events
- EventsDemoPlugin.javaMain class: Paper starts here (named as main in plugin.yml)
- JoinQuitListener.javaListener: reacts to events
- ProtectionListener.javaListener: reacts to events
- resources/Files copied into the jar as they are
- plugin.ymlyou are hereTells Paper the plugin's name, version and main class
- java/com/example/eventsdemo/Package com.example.eventsdemo
- build.gradle.ktsThe build recipe: Paper 26.3 API, Java 25, how the jar is made
- gradle.propertiesVersion numbers used by the build
- gradlew.batRuns Gradle on Windows without installing it
- settings.gradle.ktsThe project's name
- src/main/
Gray files come with the project template; you rarely edit them. Open the whole project in the Compile Lab, or download it from the project page.
1name: EventsDemo2version: '1.0.0'3main: com.example.eventsdemo.EventsDemoPlugin4api-version: '26.3'5description: Join and quit messages, a welcome title, diamond ore protection and custom death messages.6permissions:7 eventsdemo.mine.diamonds:8 description: Lets a player mine diamond ore.9 default: op- The permission's name. Starting it with your plugin's name keeps it from clashing with other plugins.
- Server operators have it automatically; everyone else needs it granted.
Not every event can be canceled. PlayerJoinEvent cannot, because by the time it fires the player is already in the world. To stop someone from joining, plugins listen to AsyncPlayerPreLoginEvent instead, which fires while the player is still connecting. The Event Finder shows a "Cancelable" badge on every event that can be canceled.
Changing death messages
PlayerDeathEvent fires when a player dies, and lets you replace the death message. This handler shows who killed whom, or a different line when no player was involved:
Where this file livesevents-demosrcmainjavacomexampleeventsdemoDeathListener.java
The package com.example.eventsdemo is the folder path com/example/eventsdemo inside src/main/java: every dot in the package name is one folder. IntelliJ creates these folders for you when you make a new package.
- events-demo/
- src/main/
- java/com/example/eventsdemo/Package com.example.eventsdemo
- DeathListener.javayou are hereListener: reacts to events
- EventsDemoPlugin.javaMain class: Paper starts here (named as main in plugin.yml)
- JoinQuitListener.javaListener: reacts to events
- ProtectionListener.javaListener: reacts to events
- resources/Files copied into the jar as they are
- plugin.ymlTells Paper the plugin's name, version and main class
- java/com/example/eventsdemo/Package com.example.eventsdemo
- build.gradle.ktsThe build recipe: Paper 26.3 API, Java 25, how the jar is made
- gradle.propertiesVersion numbers used by the build
- gradlew.batRuns Gradle on Windows without installing it
- settings.gradle.ktsThe project's name
- src/main/
Gray files come with the project template; you rarely edit them. Open the whole project in the Compile Lab, or download it from the project page.
1package com.example.eventsdemo;2 3import net.kyori.adventure.text.minimessage.MiniMessage;4import net.kyori.adventure.text.minimessage.tag.resolver.Placeholder;5import org.bukkit.entity.Player;6import org.bukkit.event.EventHandler;7import org.bukkit.event.Listener;8import org.bukkit.event.entity.PlayerDeathEvent;9 10public final class DeathListener implements Listener {11 12 private static final MiniMessage MINI_MESSAGE = MiniMessage.miniMessage();13 14 @EventHandler15 public void onDeath(PlayerDeathEvent event) {16 Player player = event.getPlayer();17 Player killer = player.getKiller();18 if (killer == null) {19 event.deathMessage(MINI_MESSAGE.deserialize("<gray><name> ran out of luck.",20 Placeholder.unparsed("name", player.getName())));21 return;22 }23 event.deathMessage(MINI_MESSAGE.deserialize("<red><killer></red> <gray>defeated</gray> <red><name>",24 Placeholder.unparsed("killer", killer.getName()),25 Placeholder.unparsed("name", player.getName())));26 }27}- The player who landed the final hit. If a creeper, lava or a fall killed the player, there is no killer and this is
null. - Always check for
nullbefore using the killer, or your handler crashes with aNullPointerExceptionthe first time someone falls off a cliff. - Two placeholders in one message: one for the killer, one for the player who died.
Steve ran out of luck.
One plugin, many listeners
Real plugins split their handlers into several listener classes, one per topic, and register all of them in onEnable:
Where this file livesevents-demosrcmainjavacomexampleeventsdemoEventsDemoPlugin.java
The package com.example.eventsdemo is the folder path com/example/eventsdemo inside src/main/java: every dot in the package name is one folder. IntelliJ creates these folders for you when you make a new package.
- events-demo/
- src/main/
- java/com/example/eventsdemo/Package com.example.eventsdemo
- DeathListener.javaListener: reacts to events
- EventsDemoPlugin.javayou are hereMain class: Paper starts here (named as main in plugin.yml)
- JoinQuitListener.javaListener: reacts to events
- ProtectionListener.javaListener: reacts to events
- resources/Files copied into the jar as they are
- plugin.ymlTells Paper the plugin's name, version and main class
- java/com/example/eventsdemo/Package com.example.eventsdemo
- build.gradle.ktsThe build recipe: Paper 26.3 API, Java 25, how the jar is made
- gradle.propertiesVersion numbers used by the build
- gradlew.batRuns Gradle on Windows without installing it
- settings.gradle.ktsThe project's name
- src/main/
Gray files come with the project template; you rarely edit them. Open the whole project in the Compile Lab, or download it from the project page.
1package com.example.eventsdemo;2 3import org.bukkit.plugin.PluginManager;4import org.bukkit.plugin.java.JavaPlugin;5 6public final class EventsDemoPlugin extends JavaPlugin {7 8 @Override9 public void onEnable() {10 PluginManager pluginManager = getServer().getPluginManager();11 pluginManager.registerEvents(new JoinQuitListener(), this);12 pluginManager.registerEvents(new ProtectionListener(), this);13 pluginManager.registerEvents(new DeathListener(), this);14 getLogger().info("Listening for joins, quits, block breaks and deaths.");15 }16}- Saving the plugin manager in a variable avoids writing
getServer().getPluginManager()three times. - Each listener is registered once. Registering the same listener twice would make its handlers run twice per event.
- Prints a line in the server console, which helps you confirm the plugin started.
- events-demo/
- src/
- main/
- java/
- com/example/eventsdemo/
- EventsDemoPlugin.javaThe main class: registers the three listeners
- JoinQuitListener.javaJoin and quit messages and the welcome title
- ProtectionListener.javaStops diamond ore from being mined without permission
- DeathListener.javaCustom death messages
- com/example/eventsdemo/
- resources/
- plugin.ymlPlugin name, version, main class and the diamond permission
- java/
- main/
- src/
You can download this whole example as a Gradle project, open it in IntelliJ, and run it, or open it in the Compile Lab to change it and compile it right in your browser.
Mistakes to avoid
- Forgetting to register the listener. Nothing happens and no error appears. Check
onEnablefirst. - Forgetting
@EventHandler. The method looks right but never runs. - Listening to a parent event that has no handler list, such as
PlayerEvent. Paper refuses with "Unable to find handler list for event org.bukkit.event.player.PlayerEvent. Static getHandlerList method required!". Listen to the specific event instead. - Doing heavy work in
PlayerMoveEvent. It fires many times per second for every player. Keep that handler tiny, or check whether the player actually changed blocks first. - Assuming something cannot be
null.getKiller(), the clicked block inPlayerInteractEventand many other values can be empty. Read the method's description (hover it in a code block) before using it. - Touching the world from
AsyncChatEvent. Events whose names start with "Async" run on a different thread, where most of the Paper API is off limits. Threads, performance and lag explains why.
Welcome back
Change the welcome listener so new players see "Welcome to the server for the first time" and returning players see "Welcome back", each with their name.
Hint 1
Players have a method that says whether they have joined this server before. Type player.has and look at what autocomplete offers.
Hint 2
Use player.hasPlayedBefore() inside an if and send a different message in each branch.
Show the solution
hasPlayedBefore() returns true when this server already has saved data for the player. The name goes in with a placeholder, exactly like the join message example.
Where this file livesevents-exercisesrcmainjavacomexampleeventsexerciseWelcomeBackListener.java
The package com.example.eventsexercise is the folder path com/example/eventsexercise inside src/main/java: every dot in the package name is one folder. IntelliJ creates these folders for you when you make a new package.
- events-exercise/
- src/main/
- java/com/example/eventsexercise/Package com.example.eventsexercise
- EventsExercisePlugin.javaMain class: Paper starts here (named as main in plugin.yml)
- WelcomeBackListener.javayou are hereListener: reacts to events
- resources/Files copied into the jar as they are
- plugin.ymlTells Paper the plugin's name, version and main class
- java/com/example/eventsexercise/Package com.example.eventsexercise
- build.gradle.ktsThe build recipe: Paper 26.3 API, Java 25, how the jar is made
- gradle.propertiesVersion numbers used by the build
- gradlew.batRuns Gradle on Windows without installing it
- settings.gradle.ktsThe project's name
- src/main/
Gray files come with the project template; you rarely edit them. Open the whole project in the Compile Lab, or download it from the project page.
1package com.example.eventsexercise;2 3import net.kyori.adventure.text.minimessage.tag.resolver.Placeholder;4import org.bukkit.entity.Player;5import org.bukkit.event.EventHandler;6import org.bukkit.event.Listener;7import org.bukkit.event.player.PlayerJoinEvent;8 9public final class WelcomeBackListener implements Listener {10 11 @EventHandler12 public void onJoin(PlayerJoinEvent event) {13 Player player = event.getPlayer();14 if (player.hasPlayedBefore()) {15 player.sendRichMessage("<green>Welcome back, <name>!", Placeholder.unparsed("name", player.getName()));16 } else {17 player.sendRichMessage("<gold>Welcome to the server for the first time, <name>!",18 Placeholder.unparsed("name", player.getName()));19 }20 }21}- The two branches send two different messages.
Protect a second block
Extend ProtectionListener so players without the permission cannot break beacons either.
Hint
Beacons are Material.BEACON. Add another condition to isDiamondOre, and give the variable a better name while you are there.
Show the solution
Add the beacon check with || ("or") and rename the variable so it still describes what it holds:
1boolean isProtected = block.getType() == Material.DIAMOND_ORE2 || block.getType() == Material.DEEPSLATE_DIAMOND_ORE3 || block.getType() == Material.BEACON;4if (!isProtected) {5 return;6}The message should change too, for example to "You need permission to break this block here."
Recap
- An event is an object Paper creates when something happens in the game.
- A listener is a class that
implements Listener; its event handlers are methods with@EventHandlerand one event parameter. - Register every listener in
onEnablewithgetServer().getPluginManager().registerEvents(listener, this). - Events give you information (
getPlayer(),getBlock()) and often let you change the outcome (joinMessage,deathMessage). - Cancelable events stop the action with
setCancelled(true). Always tell the player why. - Use the Event Finder to discover the right event for an idea.
Quick quiz
Your listener class is perfect, but nothing happens when players join. What is the most likely cause?
Paper only calls listeners that were registered withregisterEvents. The method name does not matter at all; only the@EventHandlerannotation and the parameter type do.How does Paper know that
onBreak(BlockBreakEvent event)should run when a block breaks?Paper reads the type of the single parameter. You could call the methodbananaand it would still run for every block break.What does
event.setCancelled(true)do in aBlockBreakEventhandler?Canceling tells Paper not to finish the action. The block stays, and nothing drops. Removing a block without drops is a different job, done withevent.setDropItems(false).In a
PlayerDeathEventhandler, why must you checkplayer.getKiller()fornull?getKiller()only returns a player when another player caused the death. Using anullvalue as if it were a player crashes your handler with aNullPointerException.Which event should a plugin use to stop a banned name from joining at all?
PlayerJoinEventcannot be canceled, because the player is already in the world when it fires. The pre-login event runs earlier and can refuse the connection with a message.
Next steps
- Event priorities and canceling: what happens when several plugins handle the same event.
- Commands, part 1: let players type
/helloto run your code. - Practice Arena: the first Plugin Path challenges are about exactly this page.