--- title: Block Events type: docs weight: 3 --- Events triggered by block interactions and changes. These events use the ECS (Entity Component System) pattern. ## Breaking & Placing Events ### BreakBlockEvent {{< badge "Cancellable" >}} {{< badge "ECS" >}} Fired when a block is being broken. **Package:** `com.hypixel.hytale.server.core.event.events.ecs` {{< tabs items="Fields,Methods,Example" >}} {{< tab >}} | Field | Type | Description | |-------|------|-------------| | targetBlock | `Vector3i` | Block position | | blockType | `BlockType` | Type of block | | itemInHand | `ItemStack` | Tool being used | {{< /tab >}} {{< tab >}} - `getTargetBlock()` - Returns block position - `getBlockType()` - Returns block type - `getItemInHand()` - Returns tool being used - `isCancelled()` - Check if cancelled - `setCancelled(boolean)` - Cancel breaking {{< /tab >}} {{< tab >}} ```java getEventRegistry().register(BreakBlockEvent.class, event -> { Vector3i position = event.getTargetBlock(); BlockType blockType = event.getBlockType(); ItemStack tool = event.getItemInHand(); // Protect certain blocks if (blockType.getId().equals("special_ore")) { event.setCancelled(true); return; } // Log block break getLogger().at(Level.INFO).log("Block broken at " + position + ": " + blockType.getId()); }); ``` {{< /tab >}} {{< /tabs >}} --- ### PlaceBlockEvent {{< badge "Cancellable" >}} {{< badge "ECS" >}} Fired when a block is placed. **Package:** `com.hypixel.hytale.server.core.event.events.ecs` {{< tabs items="Fields,Methods,Example" >}} {{< tab >}} | Field | Type | Description | |-------|------|-------------| | targetBlock | `Vector3i` | Position where block will be placed | | rotation | `RotationTuple` | Block rotation/orientation | | itemInHand | `ItemStack` | Block item being placed | {{< /tab >}} {{< tab >}} - `getTargetBlock()` - Returns target position - `setTargetBlock(Vector3i)` - Change placement position - `getRotation()` - Returns block rotation - `setRotation(RotationTuple)` - Change rotation - `getItemInHand()` - Returns block item - `isCancelled()` - Check if cancelled - `setCancelled(boolean)` - Cancel placement {{< /tab >}} {{< tab >}} ```java getEventRegistry().register(PlaceBlockEvent.class, event -> { Vector3i position = event.getTargetBlock(); RotationTuple rotation = event.getRotation(); ItemStack item = event.getItemInHand(); // Check build permissions if (isProtectedArea(position)) { event.setCancelled(true); return; } // Force certain rotation event.setRotation(RotationTuple.of(Rotation.North, Rotation.None, Rotation.None)); }); ``` {{< /tab >}} {{< /tabs >}} --- ### DamageBlockEvent {{< badge "Cancellable" >}} {{< badge "ECS" >}} Fired when damage is applied to a block during breaking (before the block is fully broken). **Package:** `com.hypixel.hytale.server.core.event.events.ecs` {{< tabs items="Fields,Methods,Example" >}} {{< tab >}} | Field | Type | Description | |-------|------|-------------| | targetBlock | `Vector3i` | Block position | | blockType | `BlockType` | Type of block | | itemInHand | `ItemStack` | Tool being used (may be null) | | currentDamage | `float` | Damage already applied to block | | damage | `float` | Damage being applied this tick | {{< /tab >}} {{< tab >}} - `getTargetBlock()` - Returns block position - `setTargetBlock(Vector3i)` - Change target block - `getBlockType()` - Returns block type - `getItemInHand()` - Returns tool being used - `getCurrentDamage()` - Returns accumulated damage - `getDamage()` - Returns damage this tick - `setDamage(float)` - Modify damage amount - `isCancelled()` - Check if cancelled - `setCancelled(boolean)` - Cancel damage {{< /tab >}} {{< tab >}} ```java getEventRegistry().register(DamageBlockEvent.class, event -> { Vector3i position = event.getTargetBlock(); BlockType blockType = event.getBlockType(); // Make certain blocks indestructible if (blockType.getId().equals("bedrock")) { event.setCancelled(true); return; } // Reduce damage to hardened blocks if (blockType.getId().startsWith("hardened_")) { event.setDamage(event.getDamage() * 0.5f); } // Log mining progress float progress = event.getCurrentDamage() / 100f; getLogger().at(Level.INFO).log("Block " + blockType.getId() + " at " + progress + "% damage"); }); ``` {{< /tab >}} {{< /tabs >}} --- ## Interaction Events ### UseBlockEvent Base class for block use events. Has Pre and Post variants. **Package:** `com.hypixel.hytale.server.core.event.events.ecs` #### UseBlockEvent.Pre {{< badge "Cancellable" >}} Fired before a block use action occurs (right-click on block). {{< tabs items="Fields,Methods,Example" >}} {{< tab >}} | Field | Type | Description | |-------|------|-------------| | interactionType | `InteractionType` | Type of interaction | | context | `InteractionContext` | Interaction context | | targetBlock | `Vector3i` | Block position | | blockType | `BlockType` | The block type | {{< /tab >}} {{< tab >}} - `getInteractionType()` - Returns interaction type - `getContext()` - Returns interaction context - `getTargetBlock()` - Returns block position - `getBlockType()` - Returns block type - `isCancelled()` - Check if cancelled - `setCancelled(boolean)` - Cancel use action {{< /tab >}} {{< tab >}} ```java getEventRegistry().register(UseBlockEvent.Pre.class, event -> { Vector3i position = event.getTargetBlock(); BlockType blockType = event.getBlockType(); InteractionContext context = event.getContext(); // Prevent opening locked containers if (isLocked(position)) { event.setCancelled(true); } }); ``` {{< /tab >}} {{< /tabs >}} --- #### UseBlockEvent.Post Fired after a block use action completes successfully. {{< tabs items="Fields,Methods,Example" >}} {{< tab >}} | Field | Type | Description | |-------|------|-------------| | interactionType | `InteractionType` | Type of interaction | | context | `InteractionContext` | Interaction context | | targetBlock | `Vector3i` | Block position | | blockType | `BlockType` | The block type | {{< /tab >}} {{< tab >}} - `getInteractionType()` - Returns interaction type - `getContext()` - Returns interaction context - `getTargetBlock()` - Returns block position - `getBlockType()` - Returns block type {{< /tab >}} {{< tab >}} ```java getEventRegistry().register(UseBlockEvent.Post.class, event -> { Vector3i position = event.getTargetBlock(); BlockType blockType = event.getBlockType(); // Track interactions for quests if (isQuestBlock(blockType)) { completeQuestObjective("interact_with_" + blockType.getId()); } }); ``` {{< /tab >}} {{< /tabs >}} --- ## Item Events ### DropItemEvent {{< badge "Cancellable" >}} {{< badge "ECS" >}} Fired when an item is dropped. Has two variants: `Drop` and `PlayerRequest`. **Package:** `com.hypixel.hytale.server.core.event.events.ecs` #### DropItemEvent.Drop The actual drop event with item details. {{< tabs items="Fields,Methods,Example" >}} {{< tab >}} | Field | Type | Description | |-------|------|-------------| | itemStack | `ItemStack` | Item being dropped | | throwSpeed | `float` | Speed of the throw | {{< /tab >}} {{< tab >}} - `getItemStack()` - Returns the item stack - `setItemStack(ItemStack)` - Change the dropped item - `getThrowSpeed()` - Returns throw speed - `setThrowSpeed(float)` - Change throw speed - `isCancelled()` - Check if cancelled - `setCancelled(boolean)` - Cancel drop {{< /tab >}} {{< tab >}} ```java getEventRegistry().register(DropItemEvent.Drop.class, event -> { ItemStack item = event.getItemStack(); // Prevent dropping certain items if (item.getId().equals("quest_item")) { event.setCancelled(true); return; } // Modify throw speed event.setThrowSpeed(event.getThrowSpeed() * 1.5f); }); ``` {{< /tab >}} {{< /tabs >}} --- #### DropItemEvent.PlayerRequest Fired when a player requests to drop an item (before the actual drop). {{< tabs items="Fields,Methods,Example" >}} {{< tab >}} | Field | Type | Description | |-------|------|-------------| | inventorySectionId | `int` | Inventory section ID | | slotId | `short` | Slot being dropped from | {{< /tab >}} {{< tab >}} - `getInventorySectionId()` - Returns inventory section - `getSlotId()` - Returns slot index - `isCancelled()` - Check if cancelled - `setCancelled(boolean)` - Cancel request {{< /tab >}} {{< tab >}} ```java getEventRegistry().register(DropItemEvent.PlayerRequest.class, event -> { int sectionId = event.getInventorySectionId(); short slot = event.getSlotId(); // Prevent dropping from certain slots if (sectionId == Inventory.HOTBAR && slot == 0) { event.setCancelled(true); // Can't drop first hotbar item } }); ``` {{< /tab >}} {{< /tabs >}} --- ### InteractivelyPickupItemEvent {{< badge "Cancellable" >}} {{< badge "ECS" >}} Fired when a player picks up an item interactively (manual pickup, not auto-pickup). **Package:** `com.hypixel.hytale.server.core.event.events.ecs` {{< tabs items="Fields,Methods,Example" >}} {{< tab >}} | Field | Type | Description | |-------|------|-------------| | itemStack | `ItemStack` | The item being picked up | {{< /tab >}} {{< tab >}} - `getItemStack()` - Returns the item stack - `setItemStack(ItemStack)` - Change the item being picked up - `isCancelled()` - Check if cancelled - `setCancelled(boolean)` - Cancel pickup {{< /tab >}} {{< tab >}} ```java getEventRegistry().register(InteractivelyPickupItemEvent.class, event -> { ItemStack item = event.getItemStack(); // Prevent picking up quest items that don't belong to player if (item.getId().startsWith("quest_") && !canPickupQuestItem(player, item)) { event.setCancelled(true); return; } // Transform items when picking up if (item.getId().equals("raw_ore")) { event.setItemStack(item.withCount(item.getCount() * 2)); } }); ``` {{< /tab >}} {{< /tabs >}} --- ## Crafting Events ### CraftRecipeEvent {{< badge "Cancellable" >}} {{< badge "ECS" >}} Fired when a crafting recipe is executed. Has Pre and Post variants. **Package:** `com.hypixel.hytale.server.core.event.events.ecs` {{< tabs items="Fields,Methods,Example" >}} {{< tab >}} | Field | Type | Description | |-------|------|-------------| | craftedRecipe | `CraftingRecipe` | The recipe being crafted | | quantity | `int` | Number of items being crafted | {{< /tab >}} {{< tab >}} - `getCraftedRecipe()` - Returns the crafting recipe - `getQuantity()` - Returns craft quantity - `isCancelled()` - Check if cancelled (Pre only) - `setCancelled(boolean)` - Cancel crafting (Pre only) {{< /tab >}} {{< tab >}} ```java // Pre-craft check getEventRegistry().register(CraftRecipeEvent.Pre.class, event -> { CraftingRecipe recipe = event.getCraftedRecipe(); int quantity = event.getQuantity(); // Block certain recipes if (isRestrictedRecipe(recipe)) { event.setCancelled(true); } }); // Post-craft tracking getEventRegistry().register(CraftRecipeEvent.Post.class, event -> { CraftingRecipe recipe = event.getCraftedRecipe(); int quantity = event.getQuantity(); // Track crafting statistics incrementCraftCount(recipe.getId(), quantity); }); ``` {{< /tab >}} {{< /tabs >}} --- ## Other Events ### SwitchActiveSlotEvent {{< badge "Cancellable" >}} {{< badge "ECS" >}} Fired when a player switches their active hotbar slot. **Package:** `com.hypixel.hytale.server.core.event.events.ecs` {{< tabs items="Fields,Methods,Example" >}} {{< tab >}} | Field | Type | Description | |-------|------|-------------| | inventorySectionId | `int` | The inventory section ID | | previousSlot | `int` | The slot before switching | | newSlot | `byte` | The target slot | | serverRequest | `boolean` | If server initiated the switch | {{< /tab >}} {{< tab >}} - `getInventorySectionId()` - Returns inventory section - `getPreviousSlot()` - Returns previous slot index - `getNewSlot()` - Returns new slot index - `setNewSlot(byte)` - Change the target slot - `isServerRequest()` - Check if server initiated - `isClientRequest()` - Check if client initiated - `isCancelled()` - Check if cancelled - `setCancelled(boolean)` - Cancel slot switch {{< /tab >}} {{< tab >}} ```java getEventRegistry().register(SwitchActiveSlotEvent.class, event -> { int fromSlot = event.getPreviousSlot(); int toSlot = event.getNewSlot(); // Prevent switching during cooldown if (isOnCooldown(player)) { event.setCancelled(true); return; } // Track slot usage for analytics if (event.isClientRequest()) { trackSlotSwitch(player, fromSlot, toSlot); } }); ``` {{< /tab >}} {{< /tabs >}} --- ### DiscoverZoneEvent {{< badge "ECS" >}} Fired when a player discovers a new zone. Has a `Display` variant that is cancellable. **Package:** `com.hypixel.hytale.server.core.event.events.ecs` {{< tabs items="Fields,Methods,Example" >}} {{< tab >}} | Field | Type | Description | |-------|------|-------------| | discoveryInfo | `ZoneDiscoveryInfo` | Information about the discovered zone | {{< /tab >}} {{< tab >}} - `getDiscoveryInfo()` - Returns zone discovery info - `isCancelled()` - Check if cancelled (Display only) - `setCancelled(boolean)` - Cancel display (Display only) {{< /tab >}} {{< tab >}} ```java // Cancel zone discovery notification display getEventRegistry().register(DiscoverZoneEvent.Display.class, event -> { WorldMapTracker.ZoneDiscoveryInfo info = event.getDiscoveryInfo(); // Hide certain zones from discovery UI if (isHiddenZone(info)) { event.setCancelled(true); return; } // Log discovery for achievements recordZoneDiscovery(player, info); }); ``` {{< /tab >}} {{< /tabs >}} --- ### ChangeGameModeEvent {{< badge "Cancellable" >}} {{< badge "ECS" >}} Fired when an entity's game mode changes. **Package:** `com.hypixel.hytale.server.core.event.events.ecs` {{< tabs items="Fields,Methods,Example" >}} {{< tab >}} | Field | Type | Description | |-------|------|-------------| | gameMode | `GameMode` | The new game mode | {{< /tab >}} {{< tab >}} - `getGameMode()` - Returns the new game mode - `setGameMode(GameMode)` - Change the target game mode - `isCancelled()` - Check if cancelled - `setCancelled(boolean)` - Cancel mode change {{< /tab >}} {{< tab >}} ```java getEventRegistry().register(ChangeGameModeEvent.class, event -> { GameMode newMode = event.getGameMode(); // Prevent creative mode in certain worlds if (newMode == GameMode.CREATIVE && isRestrictedWorld(player.getWorld())) { event.setCancelled(true); player.sendMessage(Message.raw("Creative mode not allowed here!")); return; } // Force adventure mode override if (shouldForceAdventure(player)) { event.setGameMode(GameMode.ADVENTURE); } getLogger().at(Level.INFO).log(player.getDisplayName() + " changed to " + newMode); }); ``` {{< /tab >}} {{< /tabs >}} --- ## Practical Examples ### Block Logger Plugin ```java public class BlockLoggerPlugin extends JavaPlugin { @Override public void start() { getEventRegistry().register(PlaceBlockEvent.class, event -> { logAction("PLACE", event.getTargetBlock(), event.getItemInHand()); }); getEventRegistry().register(BreakBlockEvent.class, event -> { logAction("BREAK", event.getTargetBlock(), event.getBlockType()); }); } private void logAction(String action, Vector3i pos, Object data) { getLogger().at(Level.INFO).log(String.format("%s: %d,%d,%d - %s", action, pos.x(), pos.y(), pos.z(), data)); } } ``` ### Region Protection System ```java public class RegionPlugin extends JavaPlugin { private final Set regions = new HashSet<>(); @Override public void start() { // Protect against breaking getEventRegistry().register(EventPriority.EARLY, BreakBlockEvent.class, event -> { if (isInProtectedRegion(event.getTargetBlock())) { event.setCancelled(true); } }); // Protect against placing getEventRegistry().register(EventPriority.EARLY, PlaceBlockEvent.class, event -> { if (isInProtectedRegion(event.getTargetBlock())) { event.setCancelled(true); } }); // Protect against use getEventRegistry().register(EventPriority.EARLY, UseBlockEvent.Pre.class, event -> { if (isInProtectedRegion(event.getTargetBlock())) { event.setCancelled(true); } }); } private boolean isInProtectedRegion(Vector3i pos) { return regions.stream().anyMatch(r -> r.contains(pos)); } } ``` ### Custom Ore System ```java public class CustomOrePlugin extends JavaPlugin { private final Random random = new Random(); @Override public void start() { getEventRegistry().register(BreakBlockEvent.class, event -> { BlockType blockType = event.getBlockType(); if (isCustomOre(blockType)) { // Apply fortune multiplier ItemStack tool = event.getItemInHand(); int fortuneLevel = getFortuneLevel(tool); int dropMultiplier = 1 + random.nextInt(fortuneLevel + 1); // Schedule custom drops Vector3i pos = event.getTargetBlock(); scheduleOreDrops(pos, blockType, dropMultiplier); } }); } private boolean isCustomOre(BlockType blockType) { return blockType.getId().startsWith("custom_ore_"); } } ``` ### Anti-Grief System ```java public class AntiGriefPlugin extends JavaPlugin { private final Map recentActions = new HashMap<>(); @Override public void start() { // Track block breaks for rollback getEventRegistry().register(BreakBlockEvent.class, event -> { Vector3i pos = event.getTargetBlock(); BlockType blockType = event.getBlockType(); recentActions.put(pos, new BlockAction( ActionType.BREAK, blockType, System.currentTimeMillis() )); }); // Track block places for rollback getEventRegistry().register(PlaceBlockEvent.class, event -> { Vector3i pos = event.getTargetBlock(); recentActions.put(pos, new BlockAction( ActionType.PLACE, null, System.currentTimeMillis() )); }); } public void rollback(World world, int seconds) { long cutoff = System.currentTimeMillis() - (seconds * 1000L); recentActions.entrySet().stream() .filter(e -> e.getValue().timestamp() >= cutoff) .forEach(e -> { Vector3i pos = e.getKey(); BlockAction action = e.getValue(); if (action.type() == ActionType.PLACE) { // Remove placed blocks world.breakBlock(pos.x(), pos.y(), pos.z()); } else if (action.blockType() != null) { // Restore broken blocks world.setBlock(pos.x(), pos.y(), pos.z(), action.blockType()); } }); recentActions.clear(); } record BlockAction(ActionType type, BlockType blockType, long timestamp) {} enum ActionType { BREAK, PLACE } } ``` ## Best Practices {{< callout type="info" >}} **Block Event Guidelines:** - Use `EventPriority.EARLY` for protection systems - ECS events provide detailed control over block operations - Always validate positions before modifying blocks - Consider chunk load state when working with distant blocks - Clean up tracking data when no longer needed {{< /callout >}} {{< callout type="warning" >}} **Performance Note:** Block events can fire very frequently. Avoid expensive operations in handlers and cache results where possible. {{< /callout >}}