Events
Minecraft Java Plugin Claude SkillsFrom the repository description
npx -y skills add MrPippi/MJP-Claude-Skills --skill eventsAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
One thing to look at
- 1 stars1 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.
SKILL.md
5.9 KB, ~1.3k tokens by cl100k_base, as published. Nobody here has run it
Events Skill — Paper
Purpose
Reference this skill when implementing event listeners on a Paper server. Covers event registration, priority ordering, cancellation, and thread-safety rules for all Bukkit/Paper events.
When to Use This Skill
- Adding a listener for any
org.bukkit.event.*orio.papermc.paper.event.*event - Deciding event priority for overlapping listeners
- Handling cancellable events correctly
- Working with async events (especially chat)
API Quick Reference
| Class / Method | Purpose | Notes |
|---|---|---|
Listener (interface) | Marker interface for event listener classes | Required on listener class |
@EventHandler | Marks a method as an event handler | Method must have exactly one event parameter |
EventPriority | Controls listener order: LOWEST → LOW → NORMAL → HIGH → HIGHEST → MONITOR | MONITOR = read-only, never cancel here |
ignoreCancelled = true | Skip handler if event already cancelled | Recommended for most handlers |
PluginManager#registerEvents(Listener, Plugin) | Register all @EventHandler methods in a listener | Call once in onEnable |
HandlerList#unregisterAll(Plugin) | Unregister all listeners for a plugin | Called automatically on onDisable |
Event#isCancelled() | Check if event is already cancelled | |
Cancellable#setCancelled(boolean) | Cancel the event | Prevents default action |
AsyncChatEvent | Paper's modern async chat event | Replaces deprecated AsyncPlayerChatEvent |
Code Pattern
package com.yourorg.myplugin.events;
import io.papermc.paper.event.player.AsyncChatEvent;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.format.NamedTextColor;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener;
import org.bukkit.event.player.PlayerJoinEvent;
import org.bukkit.event.player.PlayerQuitEvent;
import org.bukkit.plugin.java.JavaPlugin;
public class PlayerJoinListener implements Listener {
private final JavaPlugin plugin;
public PlayerJoinListener(JavaPlugin plugin) {
this.plugin = plugin;
}
// Standard listener: NORMAL priority, skip if already cancelled
@EventHandler(priority = EventPriority.NORMAL, ignoreCancelled = true)
public void onPlayerJoin(PlayerJoinEvent event) {
Player player = event.getPlayer();
// Set custom join message using Adventure components
event.joinMessage(Component.text("+ ")
.color(NamedTextColor.GREEN)
.append(Component.text(player.getName())
.color(NamedTextColor.WHITE)));
}
@EventHandler(priority = EventPriority.NORMAL)
public void onPlayerQuit(PlayerQuitEvent event) {
Player player = event.getPlayer();
event.quitMessage(Component.text("- ")
.color(NamedTextColor.RED)
.append(Component.text(player.getName())
.color(NamedTextColor.WHITE)));
}
// Async chat event — DO NOT call Bukkit object mutations here
@EventHandler(priority = EventPriority.NORMAL, ignoreCancelled = true)
public void onChat(AsyncChatEvent event) {
Player player = event.getPlayer();
Component originalMessage = event.message();
// Reading player data: safe (immutable snapshot)
String name = player.getName();
// Modifying the message: safe (event object, not Bukkit world state)
event.message(Component.text("[Chat] ")
.color(NamedTextColor.GRAY)
.append(originalMessage));
// NOT safe here: player.teleport(), world.dropItem(), etc.
// If you must do Bukkit ops, schedule back to main thread:
// Bukkit.getScheduler().runTask(plugin, () -> { ... });
}
}
Registering the listener in your main class:
@Override
public void onEnable() {
getServer().getPluginManager().registerEvents(new PlayerJoinListener(this), this);
}
Common Pitfalls
-
Calling Bukkit methods from async event handlers:
AsyncChatEventfires on an async thread. Callingplayer.teleport(),world.dropItem(), or any world-mutating method will throwIllegalStateExceptionor cause data corruption. Schedule back to main thread withBukkit.getScheduler().runTask(plugin, runnable). -
Using
MONITORpriority to cancel events:MONITORis for read-only observation after all other plugins have processed the event. Cancelling atMONITORis undefined behavior and may be ignored. -
Not using
ignoreCancelled = true: Without it, your handler fires even when another plugin has already cancelled the event, potentially overriding that decision. AddignoreCancelled = trueunless you specifically need to handle already-cancelled events. -
Registering listeners multiple times: Calling
registerEvents()twice on the same listener instance doubles all event firings. Register each listener exactly once inonEnable. -
Still using
AsyncPlayerChatEvent: This event is deprecated in Paper 1.19+ and removed in some builds. UseAsyncChatEventfromio.papermc.paper.event.playerinstead.
Version Notes
- 1.21:
AsyncChatEventis the standard chat event.AsyncPlayerChatEventis deprecated. - 1.21.1: No breaking changes to event API vs 1.21.
- Both:
PlayerMoveEventfires very frequently (every tick a player is loaded). Useevent.hasExplicitlyMoved()orevent.hasChangedPosition()to filter out head rotations.
Related Skills
- player-events.md — Detailed PlayerJoinEvent, PlayerMoveEvent, PlayerInteractEvent reference
- world-events.md — Block and entity events
- custom-events.md — Creating and firing your own events
- async-events.md — Threading rules for async events
- ../commands/SKILL.md — Command registration
What ships with it: 4 files
29.6 KB alongside SKILL.md
- async-events.md7.5 KB
- custom-events.md6.6 KB
- player-events.md8.2 KB
- world-events.md7.2 KB