Files
Documentation/content/core-concepts/events/event-reference/block-events.en.md
2026-01-20 20:33:59 +01:00

20 KiB

title, type, weight
title type weight
Block Events docs 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 >}}
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 >}}
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 >}}
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 >}}
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 >}}
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 >}}
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 >}}
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 >}}
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 >}}
// 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 >}}
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 >}}
// 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 >}}
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

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

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

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

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 >}}