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 std::unique_ptr<Block> whose getType() and getData() reflect the world right now:
std::unique_ptr<es::Block> block = overworld->getBlockAt(100, 64, -200);
// or, from a location:
block = overworld->getBlockAt(loc);
block->getType(); // "minecraft:stone" - the block's identifier
block->getData(); // a std::unique_ptr<BlockData>, including its block states
block->getLocation(); // where it sits, as a Location
block->getRelative(0, 1, 0); // the block one abovegetType()is the identifier string, like"minecraft:grass_block".getData()returns astd::unique_ptr<BlockData>describing the block fully. ItsgetBlockStates()is aBlockStatesmap -std::unordered_map<std::string, std::variant<bool, std::string, int>>- of the per-block properties such as direction or waterlogging.getRelativesteps to a neighbouring block, either by an offset or by aBlockFace(BlockFace::Up,Down,North,South,East,West) and an optional distance.
auto data = block->getData();
getLogger().info("{} states={}", data->getType(), data->getBlockStates());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->setType("minecraft:stone");To place a block with specific states - a stair facing a direction, a waterlogged fence, a particular wood - build a BlockData with getServer().createBlockData(type, states) and apply it. The BlockStates map is the same shape you read back from getBlockStates():
auto data = getServer().createBlockData(
"minecraft:oak_stairs",
{{"weirdo_direction", 2}, {"upside_down_bit", false}});
block->setData(*data);createBlockData fills every property with its default, then overrides only the keys you pass, so you set just the states you care about. Both setType and setData have an overload taking a bool apply_physics; 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 getBlockStates()).
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:
class MyPlugin : public es::Plugin {
public:
void onEnable() override
{
registerEvent(&MyPlugin::onBlockBreak, *this);
}
void onBlockBreak(es::BlockBreakEvent &event)
{
getLogger().info("{} broke {}", event.getPlayer().getName(), event.getBlock().getType());
}
};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:
#include <endstone/endstone.hpp>
namespace es = endstone;
class MyPlugin : public es::Plugin {
public:
bool onCommand(es::CommandSender &sender, const es::Command &command,
const std::vector<std::string> &args) override
{
auto *player = sender.as<es::Player>();
if (player == nullptr) {
sender.sendErrorMessage("Run this in game.");
return true;
}
es::Location loc = player->getLocation();
auto block = loc.getDimension().getBlockAt(loc.getBlockX(), loc.getBlockY() - 1, loc.getBlockZ());
auto data = getServer().createBlockData("minecraft:stained_glass", {{"color", std::string("light_blue")}});
block->setData(*data);
sender.sendMessage("Placed {} at {}, {}, {}", block->getType(), block->getX(), block->getY(), block->getZ());
return true;
}
};Declare the command in your plugin.toml and see commands for how it's wired and dispatched, and events for reacting to blocks players change themselves.