---
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<Region> 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<Vector3i, BlockAction> 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 >}}
