Blocks

Read blocks at coordinates and place them with custom block states.

Blocks live inside a dimension, addressed by their x, y, z coordinates. To touch a block you first reach its dimension, then ask the dimension for the block at a position.

Read a block

Ask a dimension for the block at integer coordinates, or hand it a Location. Either way you get a live Block whose type and data reflect the world right now:

block = overworld.get_block_at(100, 64, -200)
# or, from a location:
block = overworld.get_block_at(loc)

block.type            # "minecraft:stone" - the block's identifier
block.data            # a BlockData snapshot, including its block states
block.location        # where it sits, as a Location
block.get_relative(0, 1, 0)  # the block one above
  • block.type is the identifier string, like "minecraft:grass_block".
  • block.data returns a BlockData describing the block fully. Its block_states is a dict of the per-block properties - direction, waterlogging, growth stage, and so on - keyed by name with bool, int, or str values.
  • get_relative steps to a neighbouring block, either by an offset or by a BlockFace (BlockFace.UP, DOWN, NORTH, SOUTH, EAST, WEST) and an optional distance.
data = block.data
self.logger.info(f"{data.type} states={data.block_states}")

Change a block

Set a block's type by identifier, or set its full data to carry block states. Both write straight to the world:

block.set_type("minecraft:stone")

To place a block with specific states - a stair facing a direction, a waterlogged fence, a particular wood - build a BlockData with self.server.create_block_data(type, states) and apply it. The states map is the same shape you read back from block_states:

data = self.server.create_block_data(
    "minecraft:oak_stairs",
    {"weirdo_direction": 2, "upside_down_bit": False},
)
block.set_data(data)

create_block_data fills every property with its default, then overrides only the keys you pass, so you set just the states you care about. Both set_type and set_data take an optional apply_physics flag (default True); pass False to skip the neighbour updates when you're placing many blocks and will settle physics yourself.

Block-state names and values are vanilla Bedrock data - they vary per block. An unknown state key or an out-of-range value is silently dropped or clamped, so verify the names for the block you're placing (for example with /setblock and tab-completion in game, or by reading an existing block's block_states).

React to block changes

When you care about blocks players break or place rather than ones you set yourself, handle the block events. They live on the events page - BlockBreakEvent and BlockPlaceEvent both carry the block and the player responsible:

from endstone.event import event_handler, BlockBreakEvent

class MyPlugin(Plugin):
    api_version = "0.11"

    def on_enable(self) -> None:
        self.register_events(self)

    @event_handler
    def on_block_break(self, event: BlockBreakEvent) -> None:
        self.logger.info(f"{event.player.name} broke {event.block.type}")

Putting it together

A command that places a configured block where the player is standing. It reaches the player's dimension, floors their location to a block, builds the block data once, and writes it:

src/endstone_my_plugin/my_plugin.py
from endstone.command import Command, CommandSender
from endstone.level import Location
from endstone.player import Player
from endstone.plugin import Plugin

class MyPlugin(Plugin):
    api_version = "0.11"

    commands = {
        "platform": {
            "description": "Place a glass block under your feet",
            "usages": ["/platform"],
        }
    }

    def on_command(self, sender: CommandSender, command: Command, args: list[str]) -> bool:
        if not isinstance(sender, Player):
            sender.send_error_message("Run this in game.")
            return True

        loc = sender.location
        block = loc.dimension.get_block_at(loc.block_x, loc.block_y - 1, loc.block_z)

        data = self.server.create_block_data("minecraft:stained_glass", {"color": "light_blue"})
        block.set_data(data)

        sender.send_message(f"Placed {block.type} at {block.x}, {block.y}, {block.z}")
        return True

See commands for how the command is declared and dispatched, and events for reacting to blocks players change themselves.

On this page