02 Placing, Changing, and Breaking Blocks
Place a lever on the ground, then pull it.
The first action turns air into a lever; the second does not change the block type, but only changes the lever's state from off to on. Although the two changes are different, they both ultimately need to do the same thing: write a new block state into the world.
This article focuses on what happens before and after that write. NC updates, PP updates, and their ordering will be discussed in detail in the Block Updates chapter.
1 The common entry point for changing block state
In 1.20.1, a large number of block changes ultimately go through:
It receives three key pieces of information:
pos: which coordinates to change;state: which block state to write;flags: which systems must be notified after the write.
setBlockState first checks the build-height limits, then obtains the WorldChunk that contains those coordinates, and calls WorldChunk#setBlockState to actually modify the chunk data.
Therefore, all block changes follow a strict three-step model:
2 Placing a block
When a player right-clicks to place a normal block, the main entry point is BlockItem#place.
2.1 Determining the position and state
The game does not simply take the default state and write it directly into the world. Instead, it:
- checks whether the block is enabled in the current feature set;
- checks whether the player is allowed to place at the target coordinates;
- creates an
ItemPlacementContext; - calls the block's
getPlacementStateto compute the initial state; - checks whether that state can exist there and whether it collides with entities.
For stairs, this is where facing, top/bottom half, and waterlogging are determined; for pistons, this is where facing is computed from the player's look direction; for torches, this is where the attachment position is checked.
So, "placing a block" does not mean writing the default state first and then adjusting it later. Instead, the correct state for the current environment is computed first, and only then is the write attempted.
2.2 Writing into the chunk
BlockItem#place uses, by default:
when it calls the world's setBlockState.
Based on the block coordinates, the chunk finds the corresponding subchunk and subchunk-local coordinates, then replaces the old state in the palette container with the new state. After the replacement succeeds, it also:
- updates the relevant heightmaps;
- schedules lighting checks when needed;
- calls the old state's
onStateReplaced; - calls the new state's
onBlockAdded; - creates, removes, or updates block entities;
- marks the chunk as needing to be saved.
Here, the "old state" is usually air, but a placement operation may also replace grass, snow layers, fluids, or other replaceable blocks.
2.3 Behavior after placement completes
After the state has been written successfully, BlockItem#place also:
- applies any
BlockStateTagcarried by the item; - writes the block entity NBT from the item into the new block entity;
- calls the block's
onPlaced; - triggers the placed-block advancement criterion;
- plays the placement sound;
- emits the
BLOCK_PLACEgame event; - consumes one item in non-creative mode.
These actions are not part of the block state itself. If the state write fails, the later completion logic for placement does not run either.
3 Changing the state of an existing block
Blocks such as levers, buttons, and trapdoors typically take a new state from the old state during interaction, then call setBlockState to write it back to the same position.
For example, the change can be abstracted as:
The block type is still a lever, but the BlockState in the subchunk has already been replaced with another state. Therefore, client display changes, NC updates, and save marks may still occur.
flags determine what else must happen after the write:
NOTIFY_LISTENERS: notify listeners; on the server this is commonly how clients are synchronized;NOTIFY_NEIGHBORS: emit NC updates and update comparators when needed;- if
FORCE_STATEis not set: continue running the PP update chain caused by the old and new states; - flags such as
NO_REDRAWandREDRAW_ON_MAIN_THREADcontrol client redraw behavior.
For those flags and update ordering, see The Concept of Updates and Different Types of Updates.
4 Changes to block entities
When the chunk writes a state, it compares whether the old and new states require a block entity:
- if the old block entity is no longer applicable, it is removed;
- if the new state requires a block entity and the position does not already have one, one is created through
BlockEntityProvider#createBlockEntity; - if the block entity remains applicable, its cached block state and ticker are updated.
This shows that a block entity cannot exist independently of block state. Whether a position should have a block entity is determined first by the block state at that position.
5 Player breaking a block
The entry point for a player mining a block is ServerPlayerInteractionManager#tryBreakBlock.
The server processes the following in order:
- whether the tool is allowed to mine the block;
- permission checks, adventure-mode restrictions, and operator-block restrictions;
- calling the block's
onBreak; - calling
World#removeBlock, which replaces the current coordinates with the block state corresponding to the original fluid state; - if successful, calling
onBroken; - damaging the tool in non-creative mode;
- if harvest conditions are met, calling
afterBreakto generate drops and experience.
There is one easy-to-miss detail here: after breaking a waterlogged block, the target coordinates do not necessarily become air. World#removeBlock first gets the FluidState at that position, then writes the block state corresponding to that fluid. So when water remains after breaking a waterlogged block, that is not because an extra water block was placed afterward; it is because the replacement state chosen during block removal was a fluid state.
World#breakBlock is another general-purpose breaking entry point, used by commands, updates, or other game logic. It can directly handle break effects, drops, state replacement, and the BLOCK_DESTROY game event. Do not treat it as exactly the same call path as player mining.
6 Why a chain of changes happens after the write
When a state is written into a chunk, only one thing has been completed: the answer to "what is at these coordinates now" has been changed. To keep surrounding blocks consistent with that result, the game may still need to:
- notify clients;
- re-check lighting;
- update heightmaps;
- emit NC updates;
- make neighboring blocks recompute their own states;
- update comparators;
- create or remove block entities;
- mark the chunk as needing to be saved.
That is exactly where block update theory starts: one local state write can continue propagating along the relationships between blocks.
This article only identifies the starting point of that propagation. Its range, direction, and ordering will be expanded in later chapters.
7 Summary
- Many block changes ultimately write into the world through
World#setBlockState. - Before placement, the initial state is computed and checked for validity.
- The actual block state is stored inside subchunks.
- A write handles heightmaps, lighting, old/new block callbacks, and block entities.
flagscontrol later behavior such as client notification, NC updates, and PP updates.- Changing a property of the same block type is still a write to world data.
- Player breaking and generic
World#breakBlockuse different entry points. - When removing a waterlogged block, the replacement state can be fluid rather than air.
8 Source Analysis
8.1 setBlockState: the common entry point for all changes
World#setBlockState is the core method at the "write a block state" level:
Three steps: first write to the chunk, then decide which systems to notify based on flags, and finally run the PP update chain. maxUpdateDepth limits how far PP updates may propagate (default 512).
8.2 WorldChunk.setBlockState: where the data is really written
Pay attention to the defensive check in steps 5 through 7: recursive setBlockState may be triggered inside the onStateReplaced callback, so step 6 reads the chunk again to confirm that the current state still matches the new block. If it does not, the method returns null early, and the outer World#setBlockState also returns false.
8.3 BlockItem.place: the full placement flow
Step 3 here confirms the pattern of "compute the appropriate state first, then write it." Inside step 4, the internal place method calls setBlockState with NOTIFY_ALL | REDRAW_ON_MAIN_THREAD by default. Step 5's placeFromNbt and onPlaced are only run after the write succeeds — if the write fails, the whole placement flow stops and all later work is skipped.
8.4 World.removeBlock: remove the block, replace it with fluid
This is where the behavior of water remaining after a waterlogged block is broken comes from: when removing the block, the replacement state is "the block state corresponding to the fluid at that position," not fixed air.
8.5 World.breakBlock: a general breaking entry point
Unlike removeBlock, breakBlock adds one more step: it handles drops before replacing the block state.
8.6 ServerPlayerInteractionManager.tryBreakBlock: player breaking entry point
The flow is clear: permission checks -> onBreak -> removeBlock (which internally calls setBlockState) -> onBroken -> tool damage -> drops. Note that removeBlock(false) passes move=false, so the MOVED flag is not set.
8.7 Reference class list
net.minecraft.world.Worldnet.minecraft.world.chunk.WorldChunknet.minecraft.world.chunk.ChunkSectionnet.minecraft.item.BlockItemnet.minecraft.block.Blocknet.minecraft.block.BlockEntityProvidernet.minecraft.server.network.ServerPlayerInteractionManager
