Skip to content

Block Break & Place ​

Hytale provides cancellable events for block breaking and placing, allowing plugins to control building and destruction.

API Reference

See full API: EntityEventSystem

Concepts

Before reading this, familiarize yourself with the Event System.

Events Overview ​

EventDescriptionCancellable
BreakBlockEventFired when a block is fully brokenYes
PlaceBlockEventFired when a block is placedYes
DamageBlockEventFired when a block takes damage (mining progress)Yes

Handling Block Break[^1] ​

Use an EntityEventSystem to listen for break events:

java
import com.hypixel.hytale.component.*;
import com.hypixel.hytale.component.query.Query;
import com.hypixel.hytale.component.system.EntityEventSystem;
import com.hypixel.hytale.math.vector.Vector3i;
import com.hypixel.hytale.server.core.asset.type.blocktype.config.BlockType;
import com.hypixel.hytale.server.core.entity.entities.Player;
import com.hypixel.hytale.server.core.event.events.ecs.BreakBlockEvent;
import javax.annotation.Nonnull;

public class BlockBreakHandler extends EntityEventSystem<BreakBlockEvent> {

    @Nonnull
    @Override
    public Query getQuery() {
        return Player.getComponentType();
    }

    @Nonnull
    @Override
    public Class<BreakBlockEvent> getEventType() {
        return BreakBlockEvent.class;
    }

    @Override
    public void handle(int index, @Nonnull ArchetypeChunk chunk, @Nonnull Store store,
                       @Nonnull CommandBuffer commandBuffer, @Nonnull BreakBlockEvent event) {
        Vector3i position = event.getTargetBlock();
        BlockType blockType = event.getBlockType();

        System.out.println("Block broken: " + blockType.getId() +
                          " at " + position.getX() + ", " + position.getY() + ", " + position.getZ());
    }
}

Handling Block Place ​

java
import com.hypixel.hytale.server.core.event.events.ecs.PlaceBlockEvent;
import com.hypixel.hytale.server.core.inventory.ItemStack;

public class BlockPlaceHandler extends EntityEventSystem<PlaceBlockEvent> {

    @Nonnull
    @Override
    public Query getQuery() {
        return Player.getComponentType();
    }

    @Nonnull
    @Override
    public Class<PlaceBlockEvent> getEventType() {
        return PlaceBlockEvent.class;
    }

    @Override
    public void handle(int index, @Nonnull ArchetypeChunk chunk, @Nonnull Store store,
                       @Nonnull CommandBuffer commandBuffer, @Nonnull PlaceBlockEvent event) {
        Vector3i position = event.getTargetBlock();
        ItemStack item = event.getItemInHand();

        if (item != null) {
            System.out.println("Placed: " + item.getItemId() +
                              " at " + position.getX() + ", " + position.getY() + ", " + position.getZ());
        }
    }
}

Preventing Block Breaking ​

Cancel the event to prevent the block from being broken:

java
public class ProtectedAreaHandler extends EntityEventSystem<BreakBlockEvent> {

    private static final Vector3i PROTECTED_MIN = new Vector3i(-50, 0, -50);
    private static final Vector3i PROTECTED_MAX = new Vector3i(50, 256, 50);

    @Nonnull
    @Override
    public Query getQuery() {
        return Player.getComponentType();
    }

    @Nonnull
    @Override
    public Class<BreakBlockEvent> getEventType() {
        return BreakBlockEvent.class;
    }

    @Override
    public void handle(int index, @Nonnull ArchetypeChunk chunk, @Nonnull Store store,
                       @Nonnull CommandBuffer commandBuffer, @Nonnull BreakBlockEvent event) {
        Vector3i pos = event.getTargetBlock();

        // Check if block is in protected area
        if (isInProtectedArea(pos)) {
            event.setCancelled(true);

            // Notify player
            Player player = (Player) chunk.getComponent(index, Player.getComponentType());
            player.getPlayerRef().sendMessage(Message.raw("You cannot break blocks here!"));
        }
    }

    private boolean isInProtectedArea(Vector3i pos) {
        return pos.getX() >= PROTECTED_MIN.getX() && pos.getX() <= PROTECTED_MAX.getX() &&
               pos.getY() >= PROTECTED_MIN.getY() && pos.getY() <= PROTECTED_MAX.getY() &&
               pos.getZ() >= PROTECTED_MIN.getZ() && pos.getZ() <= PROTECTED_MAX.getZ();
    }
}

Preventing Block Placement ​

java
public class NoBuildZoneHandler extends EntityEventSystem<PlaceBlockEvent> {

    @Nonnull
    @Override
    public Query getQuery() {
        return Player.getComponentType();
    }

    @Nonnull
    @Override
    public Class<PlaceBlockEvent> getEventType() {
        return PlaceBlockEvent.class;
    }

    @Override
    public void handle(int index, @Nonnull ArchetypeChunk chunk, @Nonnull Store store,
                       @Nonnull CommandBuffer commandBuffer, @Nonnull PlaceBlockEvent event) {
        Vector3i pos = event.getTargetBlock();

        // Prevent building above Y=200
        if (pos.getY() > 200) {
            event.setCancelled(true);

            Player player = (Player) chunk.getComponent(index, Player.getComponentType());
            player.getPlayerRef().sendMessage(Message.raw("Cannot build this high!"));
        }
    }
}

Block Damage Events ​

The DamageBlockEvent fires each time a block takes mining damage. You can modify or prevent the damage:

java
public class MiningBoostHandler extends EntityEventSystem<DamageBlockEvent> {

    @Nonnull
    @Override
    public Query getQuery() {
        return Player.getComponentType();
    }

    @Nonnull
    @Override
    public Class<DamageBlockEvent> getEventType() {
        return DamageBlockEvent.class;
    }

    @Override
    public void handle(int index, @Nonnull ArchetypeChunk chunk, @Nonnull Store store,
                       @Nonnull CommandBuffer commandBuffer, @Nonnull DamageBlockEvent event) {
        // Double mining speed
        float originalDamage = event.getDamage();
        event.setDamage(originalDamage * 2.0f);

        // Or prevent damage entirely
        // event.setCancelled(true);
    }
}

Event Properties ​

BreakBlockEvent ​

MethodReturnsDescription
getTargetBlock()Vector3iPosition of the broken block
setTargetBlock(Vector3i)voidChange which block is affected
getBlockType()BlockTypeType of block being broken
getItemInHand()ItemStackTool used (nullable)
setCancelled(boolean)voidPrevent the break
isCancelled()booleanCheck if cancelled

PlaceBlockEvent ​

MethodReturnsDescription
getTargetBlock()Vector3iPosition where block will be placed
setTargetBlock(Vector3i)voidChange placement position
getRotation()RotationTupleBlock rotation
setRotation(RotationTuple)voidChange block rotation
getItemInHand()ItemStackItem being placed (nullable)
setCancelled(boolean)voidPrevent placement
isCancelled()booleanCheck if cancelled

DamageBlockEvent ​

MethodReturnsDescription
getTargetBlock()Vector3iPosition of damaged block
getBlockType()BlockTypeType of block
getCurrentDamage()floatCurrent block health
getDamage()floatDamage being applied
setDamage(float)voidModify damage amount
getItemInHand()ItemStackTool used (nullable)
setCancelled(boolean)voidPrevent damage

Checking Block Modification Settings ​

Block modification permissions are configured per-world via GameplayConfig:

java
World world = Universe.get().getWorld("myworld");
GameplayConfig gameplayConfig = world.getGameplayConfig();

// Check if block breaking is allowed
if (!gameplayConfig.isBlockBreakingAllowed()) {
    // Block breaking is disabled in this world's config
}

// Check if block placement is allowed
if (!gameplayConfig.isBlockPlacementAllowed()) {
    // Block placement is disabled in this world's config
}

// Check if block gathering (interaction) is allowed
if (!gameplayConfig.isBlockGatheringAllowed()) {
    // Block gathering is disabled in this world's config
}

TIP

These are read-only config values. To dynamically control block modification, use event handlers to cancel BreakBlockEvent or PlaceBlockEvent as shown above.

Restricting by Block Type ​

java
public class BlockTypeRestriction extends EntityEventSystem<BreakBlockEvent> {

    // Block types that cannot be broken
    private static final Set<String> PROTECTED_BLOCKS = Set.of(
        "Block_Bedrock",
        "Block_Barrier",
        "Block_Spawner"
    );

    @Nonnull
    @Override
    public Query getQuery() {
        return Player.getComponentType();
    }

    @Nonnull
    @Override
    public Class<BreakBlockEvent> getEventType() {
        return BreakBlockEvent.class;
    }

    @Override
    public void handle(int index, @Nonnull ArchetypeChunk chunk, @Nonnull Store store,
                       @Nonnull CommandBuffer commandBuffer, @Nonnull BreakBlockEvent event) {
        String blockId = event.getBlockType().getId();

        if (PROTECTED_BLOCKS.contains(blockId)) {
            event.setCancelled(true);
        }
    }
}

Full Plugin Example ​

java
package com.example.buildprotection;

import com.hypixel.hytale.common.plugin.PluginManifest;
import com.hypixel.hytale.component.*;
import com.hypixel.hytale.component.query.Query;
import com.hypixel.hytale.component.system.EntityEventSystem;
import com.hypixel.hytale.math.vector.Vector3i;
import com.hypixel.hytale.server.core.entity.entities.Player;
import com.hypixel.hytale.server.core.event.events.ecs.BreakBlockEvent;
import com.hypixel.hytale.server.core.event.events.ecs.PlaceBlockEvent;
import com.hypixel.hytale.server.core.Message;
import com.hypixel.hytale.server.core.modules.interaction.InteractionModule;
import com.hypixel.hytale.server.core.plugin.JavaPlugin;
import com.hypixel.hytale.server.core.plugin.JavaPluginInit;
import com.hypixel.hytale.server.core.universe.world.storage.EntityStore;
import javax.annotation.Nonnull;

public class BuildProtectionPlugin extends JavaPlugin {

    public static final PluginManifest MANIFEST = PluginManifest
        .corePlugin(BuildProtectionPlugin.class)
        .depends(InteractionModule.class)
        .build();

    public BuildProtectionPlugin(@Nonnull JavaPluginInit init) {
        super(init);
    }

    @Override
    protected void setup() {
        ComponentRegistryProxy<EntityStore> registry = this.getEntityStoreRegistry();

        registry.registerSystem(new SpawnProtection());
        registry.registerSystem(new PlaceProtection());

        this.getLogger().info("BuildProtection plugin loaded!");
    }

    // Protect spawn area from block breaking
    public static class SpawnProtection extends EntityEventSystem<BreakBlockEvent> {
        private static final int SPAWN_RADIUS = 100;

        @Nonnull
        @Override
        public Query getQuery() {
            return Player.getComponentType();
        }

        @Nonnull
        @Override
        public Class<BreakBlockEvent> getEventType() {
            return BreakBlockEvent.class;
        }

        @Override
        public void handle(int index, @Nonnull ArchetypeChunk chunk, @Nonnull Store store,
                           @Nonnull CommandBuffer commandBuffer, @Nonnull BreakBlockEvent event) {
            Vector3i pos = event.getTargetBlock();

            // Protect area around world origin
            if (Math.abs(pos.getX()) < SPAWN_RADIUS && Math.abs(pos.getZ()) < SPAWN_RADIUS) {
                event.setCancelled(true);

                Player player = (Player) chunk.getComponent(index, Player.getComponentType());
                player.getPlayerRef().sendMessage(
                    Message.raw("Spawn area is protected from block breaking!")
                );
            }
        }
    }

    // Protect spawn area from block placement
    public static class PlaceProtection extends EntityEventSystem<PlaceBlockEvent> {
        private static final int SPAWN_RADIUS = 100;

        @Nonnull
        @Override
        public Query getQuery() {
            return Player.getComponentType();
        }

        @Nonnull
        @Override
        public Class<PlaceBlockEvent> getEventType() {
            return PlaceBlockEvent.class;
        }

        @Override
        public void handle(int index, @Nonnull ArchetypeChunk chunk, @Nonnull Store store,
                           @Nonnull CommandBuffer commandBuffer, @Nonnull PlaceBlockEvent event) {
            Vector3i pos = event.getTargetBlock();

            if (Math.abs(pos.getX()) < SPAWN_RADIUS && Math.abs(pos.getZ()) < SPAWN_RADIUS) {
                event.setCancelled(true);

                Player player = (Player) chunk.getComponent(index, Player.getComponentType());
                player.getPlayerRef().sendMessage(
                    Message.raw("Spawn area is protected from building!")
                );
            }
        }
    }
}

See Also ​

[^1]: See EntityEventSystem API for the handle() method signature

Unofficial documentation · Any questions? Found a mistake? Have something you want documented? Join the Discord server at the top and let us know in #hytale!