# Events Bookshelf provides custom Bukkit events that fire during key plugin interactions. All events extend `BaseEvent`, which provides a convenience `call()` method and shared `HandlerList`. --- ## BaseEvent All Bookshelf events extend `BaseEvent`, which is an abstract class extending Bukkit's `Event`. ```java public abstract class BaseEvent extends Event { public boolean call() { Bukkit.getPluginManager().callEvent(this); return this instanceof Cancellable && ((Cancellable) this).isCancelled(); } } ``` The `call()` method fires the event through Bukkit's event system and returns `true` if the event was cancelled. This allows compact event dispatching: ```java if (new PlayerChatEvent(player, prefix, message).call()) { return; // Event was cancelled by a listener } ``` --- ## Available Events ### PlayerChatEvent Fires when a player sends a chat message through Bookshelf's chat system. Implements `Cancellable`. | Field | Type | Description | |---|---|---| | `player` | `Player` | The player who sent the message. | | `prefix` | `Component` | The chat prefix component. Settable. | | `suffix` | `Component` | The chat suffix component. Settable. | | `message` | `Component` | The chat message content. Settable. | | `permission` | `String` | Permission required to see this message. Settable. | | `playerColor` | `String` | Hex color for the player name. Settable. | | `messageColor` | `String` | Hex color for the message text. Settable. | | `viewers` | `List<UUID>` | Specific recipients. Null means all players. Settable. | | `isModified` | `boolean` | Whether another plugin has modified this event. | ```java @EventHandler public void onChat(PlayerChatEvent event) { // Change message color for VIP players if (event.getPlayer().hasPermission("vip.chat")) { event.messageColor(NamedTextColor.GOLD.asHexString()); } // Restrict viewers to nearby players List<UUID> nearby = event.getPlayer().getLocation() .getNearbyPlayers(50).stream() .map(Player::getUniqueId) .toList(); event.setViewers(nearby); } ``` --- ### PlayerChatModerationEvent Fires when a player's chat message is blocked by a moderation check. Implements `Cancellable`. Cancelling this event **overrides the moderation** and allows the message through. | Field | Type | Description | |---|---|---| | `player` | `Player` | The player whose message was moderated. | | `message` | `Component` | The original chat message. | | `type` | `ModerationType` | The type of moderation that triggered. | | `detail` | `String` (nullable) | Type-specific context (see table below). | #### ModerationType Enum | Value | Triggered By | `detail` Contains | |---|---|---| | `WARNED` | Player has active warn infraction | Warn reason | | `MUTED` | Player has active mute infraction | Mute reason | | `GLOBAL_MUTE` | Server chat is muted | `null` | | `BANNED_WORD` | Message contains blacklisted word | The matched word | | `CHARACTER_SPAM` | Repeated character detection | The raw message | | `COOLDOWN` | Sending messages too fast | Cooldown duration string | | `DUPLICATE_MESSAGE` | Same message sent twice in a row | The duplicate message text | ```java @EventHandler public void onModeration(PlayerChatModerationEvent event) { // Allow VIP players to bypass cooldowns if (event.getType() == PlayerChatModerationEvent.ModerationType.COOLDOWN && event.getPlayer().hasPermission("vip.chat.bypass")) { event.setCancelled(true); // Override moderation, let message through } // Log all moderation actions getLogger().info(event.getPlayer().getName() + " blocked by " + event.getType() + (event.getDetail() != null ? ": " + event.getDetail() : "")); } ``` --- ### PlayerMessageEvent Fires when a player sends a private message (whisper). Supports both local and cross-network messages. Implements `Cancellable`. | Field | Type | Description | |---|---|---| | `player` | `Player` | The player who sent the message. | | `target` | `Player` (nullable) | The target player, if on the same server. | | `targetUniqueId` | `UUID` (nullable) | The target UUID, for cross-network messages. | | `targetName` | `String` (nullable) | The target name, for cross-network messages. | ```java @EventHandler public void onMessage(PlayerMessageEvent event) { if (event.isLocal()) { // Message sent on same server Player target = event.getTarget(); } else if (event.isCrossNetwork()) { // Message sent to another server UUID targetId = event.getTargetUniqueId(); String targetName = event.getTargetName(); } } ``` --- ### PlayerCraftCustomItemEvent Fires when a player crafts a `CustomItem` registered through the item system. | Field | Type | Description | |---|---|---| | `player` | `Player` | The player crafting the item. | | `customItem` | `CustomItem` | The custom item being crafted. | | `result` | `ItemStack` | The resulting ItemStack. | | `cancelled` | `boolean` | Whether crafting is cancelled. | ```java @EventHandler public void onCraftCustom(PlayerCraftCustomItemEvent event) { if (event.getCustomItem().id().equals("legendary_sword")) { event.getPlayer().sendMessage("You forged a legendary weapon!"); } } ``` --- ### PlayerEquipArmorEvent Fires when a player equips armor. Implements `Cancellable`. | Field | Type | Description | |---|---|---| | `player` | `Player` | The player equipping armor. | | `itemStack` | `ItemStack` | The armor piece being equipped. | | `slot` | `EquipmentSlot` | The equipment slot (HEAD, CHEST, LEGS, FEET). | ```java @EventHandler public void onEquipArmor(PlayerEquipArmorEvent event) { if (event.getSlot() == EquipmentSlot.HEAD) { event.getPlayer().sendMessage("New helmet equipped!"); } } ``` --- ### PlayerRespawnEvent Fires when a player respawns. Allows overriding the respawn location. | Field | Type | Description | |---|---|---| | `player` | `Player` | The player respawning. | | `respawnLocation` | `Location` (nullable) | Override respawn location. Null uses default. | ```java @EventHandler public void onRespawn(PlayerRespawnEvent event) { // Redirect respawn to a custom location Location lobby = new Location(Bukkit.getWorld("world"), 0, 100, 0); event.setRespawnLocation(lobby); } ``` --- ### BookshelfReloadEvent Fires when the Bookshelf plugin reloads its configuration. Contains no fields. Use this to refresh any cached data that depends on Bookshelf's config. ```java @EventHandler public void onReload(BookshelfReloadEvent event) { // Refresh cached configuration values reloadMyConfig(); } ``` --- ## Infraction Events All infraction events extend `PlayerInfractionEvent`, an abstract cancellable event with an `issuer` and `reason`. ### PlayerInfractionEvent (Base) | Field | Type | Description | |---|---|---| | `issuer` | `CommandSender` | The staff member who issued the infraction. | | `reason` | `String` | The infraction reason. Settable. | ### PlayerBanEvent Fires when a player is banned. Extends `PlayerInfractionEvent`. | Field | Type | Description | |---|---|---| | `targetUUID` | `UUID` | The banned player's UUID. | | `targetName` | `String` | The banned player's name. | | `duration` | `long` | Ban duration in milliseconds. Settable. | ### PlayerBanIPEvent Fires when an IP address is banned. Extends `PlayerInfractionEvent`. | Field | Type | Description | |---|---|---| | `ip` | `String` | The banned IP address. | | `duration` | `long` | Ban duration in milliseconds. Settable. | ### PlayerKickEvent Fires when a player is kicked. Extends `PlayerInfractionEvent`. | Field | Type | Description | |---|---|---| | `targetUUID` | `UUID` | The kicked player's UUID. | | `targetName` | `String` | The kicked player's name. | ### PlayerMuteEvent Fires when a player is muted. Extends `PlayerInfractionEvent`. | Field | Type | Description | |---|---|---| | `targetUUID` | `UUID` | The muted player's UUID. | | `targetName` | `String` | The muted player's name. | | `duration` | `long` | Mute duration in milliseconds. Settable. | ### PlayerWarnEvent Fires when a player is warned. Extends `PlayerInfractionEvent`. | Field | Type | Description | |---|---|---| | `targetUUID` | `UUID` | The warned player's UUID. | | `targetName` | `String` | The warned player's name. | ### PlayerUnbanEvent Fires when a player is unbanned. Extends `PlayerInfractionEvent`. | Field | Type | Description | |---|---|---| | `targetUUID` | `UUID` | The unbanned player's UUID. | | `targetName` | `String` | The unbanned player's name. | ### PlayerUnbanIPEvent Fires when an IP ban is removed. Extends `PlayerInfractionEvent`. | Field | Type | Description | |---|---|---| | `ip` | `String` | The unbanned IP address. | ### PlayerUnmuteEvent Fires when a player is unmuted. Extends `PlayerInfractionEvent`. | Field | Type | Description | |---|---|---| | `targetUUID` | `UUID` | The unmuted player's UUID. | | `targetName` | `String` | The unmuted player's name. | ```java @EventHandler public void onBan(PlayerBanEvent event) { // Log bans to an external service logToDiscord(event.getIssuer().getName() + " banned " + event.getTargetName()); } @EventHandler public void onMute(PlayerMuteEvent event) { // Extend mute duration for repeat offenders if (isRepeatOffender(event.getTargetUUID())) { event.setDuration(event.getDuration() * 2); } } ``` --- ## Privacy Events ### PlayerBlockEvent Fires when a player blocks another player. Implements `Cancellable`. | Field | Type | Description | |---|---|---| | `player` | `Player` | The player performing the block. | | `target` | `Player` | The player being blocked. | ### PlayerUnblockEvent Fires when a player unblocks another player. Implements `Cancellable`. | Field | Type | Description | |---|---|---| | `player` | `Player` | The player performing the unblock. | | `target` | `Player` | The player being unblocked. | ```java @EventHandler public void onBlock(PlayerBlockEvent event) { // Prevent blocking staff members if (event.getTarget().hasPermission("staff.unblockable")) { event.setCancelled(true); event.getPlayer().sendMessage("You cannot block staff members."); } } ``` --- ## Listening for Events Register listeners using standard Bukkit event handling. All Bookshelf events use the same `HandlerList` from `BaseEvent`. ```java public class MyListener implements Listener { @EventHandler public void onChat(PlayerChatEvent event) { // Handle chat } @EventHandler(priority = EventPriority.HIGH) public void onMessage(PlayerMessageEvent event) { // Handle with high priority } } // Register in onEnable() getServer().getPluginManager().registerEvents(new MyListener(), this); ``` --- ## Related Pages - [[Bookshelf/Developers/API Reference]] - Core API classes - [[Bookshelf/Developers/Overview]] - Module architecture - [[Command System]] - Command creation