---
title: Blocks
type: docs
weight: 3
---

Block manipulation is a core part of world modification in Hytale plugins. The `World` class provides block access through the `IChunkAccessorSync` interface, while `WorldChunk` implements the full `BlockAccessor` interface for rotation-aware placement.

{{< callout type="warning" >}}
**Important:** `World` does NOT implement `BlockAccessor` directly. For `placeBlock()` with rotation, you must get the chunk first using `world.getChunk()`.
{{< /callout >}}

## Getting Blocks

```java
import com.hypixel.hytale.server.core.universe.Universe;
import com.hypixel.hytale.server.core.universe.world.World;
import com.hypixel.hytale.server.core.asset.type.blocktype.config.BlockType;
import com.hypixel.hytale.math.vector.Vector3i;

World world = Universe.get().getWorld("default");

// Get block ID (int index) at position
int blockId = world.getBlock(x, y, z);
int blockId = world.getBlock(new Vector3i(x, y, z));

// Get BlockType at position
BlockType blockType = world.getBlockType(x, y, z);
BlockType blockType = world.getBlockType(new Vector3i(x, y, z));

// Check if block is empty
if (blockType == BlockType.EMPTY) {
    // Air or empty space
}
```

## Setting Blocks

The `setBlock` method takes a **String block type key**, not a BlockType object or int ID:

```java
import com.hypixel.hytale.server.core.asset.type.blocktype.config.BlockType;

World world = player.getWorld();

// Set block by block type key (string ID)
world.setBlock(x, y, z, "stone");
world.setBlock(x, y, z, "oak_planks", 0);  // with settings

// Using BlockType - get the ID string first
BlockType stone = BlockType.getAssetMap().getAsset("stone");
if (stone != null) {
    world.setBlock(x, y, z, stone.getId());  // Use getId() to get the string key
}

// Block ID index (for internal use)
int stoneIndex = BlockType.getAssetMap().getIndex("stone");
// Note: setBlock takes String, not int - use the key directly
```

## Block Placement with Rotation

{{< callout type="info" >}}
**placeBlock() is on WorldChunk, not World!** You must get the chunk first to place blocks with rotation.
{{< /callout >}}

```java
import com.hypixel.hytale.server.core.universe.world.chunk.WorldChunk;
import com.hypixel.hytale.server.core.asset.type.blocktype.config.Rotation;
import com.hypixel.hytale.server.core.asset.type.blocktype.config.RotationTuple;
import com.hypixel.hytale.math.util.ChunkUtil;

// Get the chunk containing this block position
WorldChunk chunk = world.getChunk(ChunkUtil.indexChunkFromBlock(x, z));

if (chunk != null) {
    // Place block with rotation
    // Rotation values: None, Ninety, OneEighty, TwoSeventy
    chunk.placeBlock(x, y, z, "oak_log",
        Rotation.Ninety,   // yaw (90 degrees)
        Rotation.None,     // pitch
        Rotation.None      // roll
    );

    // Place with rotation tuple and options
    chunk.placeBlock(x, y, z, "oak_log",
        RotationTuple.of(Rotation.Ninety, Rotation.None, Rotation.None),
        0,      // settings
        true    // validatePlacement
    );
}
```

### Rotation Values

The `Rotation` enum has these values (NOT cardinal directions):

| Rotation | Degrees |
|----------|---------|
| `Rotation.None` | 0° |
| `Rotation.Ninety` | 90° |
| `Rotation.OneEighty` | 180° |
| `Rotation.TwoSeventy` | 270° |

## Breaking Blocks

```java
// Break block at position (requires settings parameter)
world.breakBlock(x, y, z, 0);  // 0 = default settings
```

## BlockType Assets

BlockType assets define block properties:

```java
import com.hypixel.hytale.server.core.asset.type.blocktype.config.BlockType;
import com.hypixel.hytale.protocol.BlockMaterial;

// Get block type by ID
BlockType blockType = BlockType.getAssetMap().getAsset("stone");

if (blockType != null) {
    // Get block ID string
    String blockId = blockType.getId();

    // Get material
    BlockMaterial material = blockType.getMaterial();
}

// Get block ID index (for internal use)
int blockIndex = BlockType.getAssetMap().getIndex("stone");

// Get block from index
BlockType fromIndex = BlockType.getAssetMap().getAsset(blockIndex);
```

## Block Events

### BreakBlockEvent

Fired when a block is broken:

```java
import com.hypixel.hytale.server.core.event.events.ecs.BreakBlockEvent;
import com.hypixel.hytale.math.vector.Vector3i;
import java.util.logging.Level;

getEventRegistry().register(BreakBlockEvent.class, event -> {
    // Get break info
    Vector3i position = event.getTargetBlock();
    BlockType blockType = event.getBlockType();

    getLogger().at(Level.INFO).log("Block broken at " + position);

    // Cancel if protected
    event.setCancelled(true);
});
```

### PlaceBlockEvent

Fired when a block is placed:

```java
import com.hypixel.hytale.server.core.event.events.ecs.PlaceBlockEvent;
import com.hypixel.hytale.server.core.asset.type.blocktype.config.RotationTuple;

getEventRegistry().register(PlaceBlockEvent.class, event -> {
    // Get place info
    Vector3i position = event.getTargetBlock();
    RotationTuple rotation = event.getRotation();

    getLogger().at(Level.INFO).log("Block placed at " + position);

    // Cancel if not allowed
    event.setCancelled(true);
});
```

### DamageBlockEvent

Fired when a block is damaged (before breaking):

```java
import com.hypixel.hytale.server.core.event.events.ecs.DamageBlockEvent;

getEventRegistry().register(DamageBlockEvent.class, event -> {
    Vector3i position = event.getTargetBlock();
    BlockType blockType = event.getBlockType();

    // Cancel to prevent damage
    event.setCancelled(true);
});
```

## Block Interaction State

Blocks can have interaction states (like open/closed doors):

```java
BlockType currentBlock = world.getBlockType(x, y, z);

if (currentBlock != null) {
    // Set block to a different state
    world.setBlockInteractionState(
        new Vector3i(x, y, z),
        currentBlock,
        "open"  // state name
    );
}
```

## Working with Blocks

### Check Area for Block Type

```java
public boolean containsBlockType(World world, Vector3i min, Vector3i max, String blockTypeKey) {
    // Note: Vector3i uses getX(), getY(), getZ() - NOT x(), y(), z()
    for (int x = min.getX(); x <= max.getX(); x++) {
        for (int y = min.getY(); y <= max.getY(); y++) {
            for (int z = min.getZ(); z <= max.getZ(); z++) {
                BlockType block = world.getBlockType(x, y, z);
                if (block != null && block.getId().equals(blockTypeKey)) {
                    return true;
                }
            }
        }
    }
    return false;
}
```

### Fill Area

```java
public void fillArea(World world, Vector3i min, Vector3i max, String blockTypeKey) {
    for (int x = min.getX(); x <= max.getX(); x++) {
        for (int y = min.getY(); y <= max.getY(); y++) {
            for (int z = min.getZ(); z <= max.getZ(); z++) {
                world.setBlock(x, y, z, blockTypeKey);
            }
        }
    }
}
```

### Replace Blocks

```java
public void replaceBlocks(World world, Vector3i min, Vector3i max,
                          String fromBlock, String toBlock) {
    for (int x = min.getX(); x <= max.getX(); x++) {
        for (int y = min.getY(); y <= max.getY(); y++) {
            for (int z = min.getZ(); z <= max.getZ(); z++) {
                BlockType current = world.getBlockType(x, y, z);
                if (current != null && current.getId().equals(fromBlock)) {
                    world.setBlock(x, y, z, toBlock);
                }
            }
        }
    }
}
```

## Block Positions

```java
import com.hypixel.hytale.math.vector.Vector3i;

// Vector3i for integer block positions
Vector3i blockPos = new Vector3i(100, 64, 200);

// Get individual coordinates - use getX(), getY(), getZ()
int x = blockPos.getX();
int y = blockPos.getY();
int z = blockPos.getZ();

// Adjacent positions
Vector3i above = new Vector3i(x, y + 1, z);
Vector3i below = new Vector3i(x, y - 1, z);
Vector3i north = new Vector3i(x, y, z - 1);
Vector3i south = new Vector3i(x, y, z + 1);
Vector3i east = new Vector3i(x + 1, y, z);
Vector3i west = new Vector3i(x - 1, y, z);
```

## Best Practices

{{< callout type="info" >}}
**Block Manipulation Tips:**
- Block operations must run on the world's ticking thread
- Use `getBlockType()` for checking block types, `getBlock()` for raw IDs
- Check `BlockType.EMPTY` for air/empty blocks
- Events extend `CancellableEcsEvent` - use `setCancelled(true)` to prevent
- Consider chunk boundaries when processing large areas
- For `placeBlock()` with rotation, get the chunk first
{{< /callout >}}

```java
// Safe block modification from async context
world.execute(() -> {
    world.setBlock(x, y, z, "stone");
});
```

## API Reference

### World (IChunkAccessorSync) Methods

| Method | Returns | Description |
|--------|---------|-------------|
| `getBlock(int, int, int)` | `int` | Get block ID at position |
| `getBlock(Vector3i)` | `int` | Get block ID at position |
| `getBlockType(int, int, int)` | `BlockType?` | Get BlockType at position |
| `getBlockType(Vector3i)` | `BlockType?` | Get BlockType at position |
| `setBlock(x, y, z, String)` | `void` | Set block by type key |
| `setBlock(x, y, z, String, int)` | `void` | Set block with settings |
| `breakBlock(x, y, z, int)` | `boolean` | Break block with settings |
| `setBlockInteractionState(...)` | `void` | Set block state |

### WorldChunk (BlockAccessor) Methods

| Method | Returns | Description |
|--------|---------|-------------|
| `placeBlock(x, y, z, String, Rotation, Rotation, Rotation)` | `boolean` | Place with rotation |
| `placeBlock(x, y, z, String, RotationTuple, int, boolean)` | `boolean` | Place with options |
| `testPlaceBlock(...)` | `boolean` | Test if placement valid |
