Tile Entities

Tile entities are blocks that are capable of additional operations like autonomically crafting (furnaces and brewing stands) or provide effects (like a beacon or a note block). They also contain additional data like an inventory or text (like chests, signs or command blocks).

Identifying Tile Entities and their Type

Again, it all starts with a Location. The Location#getTileEntity() function will return the tile entity corresponding to the block or Optional.empty() if the block is not a tile entity.

import org.spongepowered.api.world.Location;
import org.spongepowered.api.world.World;

public boolean isTileEntity(Location<World> blockLoc) {
    return blockLoc.getTileEntity().isPresent();
}

The type of a tile entity can then be obtained by the TileEntity#getType() function which returns a TileEntityType. Which can then be compared similar to a BlockType. After performing this check the TileEntity variable can safely be cast to the according subtype.

import org.spongepowered.api.block.tileentity.Jukebox;
import org.spongepowered.api.block.tileentity.TileEntity;
import org.spongepowered.api.block.tileentity.TileEntityTypes;

public boolean isJukebox(TileEntity entity) {
    return entity.getType().equals(TileEntityTypes.JUKEBOX);
}

public void ejectDiscFromJukebox(TileEntity entity) {
    if (isJukebox(entity)) {
        Jukebox jukebox = (Jukebox) entity;
        jukebox.ejectRecord();
    }
}

After performing this cast, the methods provided by the particular interface can be accessed (in this example the Jukebox#ejectRecord() method). For detailed information about TileEntity subtypes and their respective methods refer to the org.spongepowered.api.block.tileentity package and its subpackages in the API.

Accessing and Modifying a Tile Entity’s Data

Similar to block states, the data stored in a tile entity is accessed using a DataManipulator. Since the kind of a data is fully described by the DataManipulator used, all data manipulation can be done with the TileEntity interface itself and does not require a cast.

The following example contains two methods to alter the data of a sign. The first method reads (if possible) the first line, the second attempts to set it and returns the boolean value indicating its success.

import org.spongepowered.api.data.manipulator.mutable.tileentity.SignData;
import org.spongepowered.api.text.Text;

import java.util.Optional;

public Optional<Text> getFirstLine(TileEntity entity) {
    Optional<SignData> data = entity.getOrCreate(SignData.class);
    if (data.isPresent()) {
        return Optional.of(data.get().lines().get(0));
    }
    return Optional.empty();
}

public boolean setFirstLine(TileEntity entity, Text line) {
    if (entity.supports(SignData.class)) {
        SignData sign = entity.getOrCreate(SignData.class).get();
        sign.set(sign.lines().set(0, line));
        entity.offer(sign);
        return true;
    }
    return false;
}

The main difference to working with a BlockState is that a tile entity is a mutable DataHolder as opposed to the immutable BlockState.

Accessing Inventories

Quite a share of tile entities come with their own inventory, most notably chests and furnaces. That inventory can not be accessed directly from the TileEntity interface. So a cast will be necessary. Since all tile entities containing an inventory extend the TileEntityCarrier interface it suffices to cast to that interface as shown below.

import org.spongepowered.api.block.tileentity.carrier.TileEntityCarrier;
import org.spongepowered.api.item.inventory.Inventory;

public void useInventory(TileEntity entity) {
    if (entity instanceof TileEntityCarrier) {
        TileEntityCarrier carrier = (TileEntityCarrier) entity;
        Inventory inventory = carrier.getInventory();
        ...
    }
}

Refer to the inventory documentation regarding the manipulation of the inventory.