The bug
When attempting to place a block above (min_y + height) (default is 320 in minecraft:overworld and 256 in minecraft:the_nether and minecraft:the_end), an error message stating that this is not possible is displayed. When attempting to place a block outwith the opposite extreme, there is no error message. Compare attached screenshots.
Code analysis
Code analysis by @unknown can be found in this comment.
Related issues
is duplicated by
relates to
Attachments
Comments


Invalid, as you usually can't get there. But in creative you can, so it's up to mojang to decide.

As of 18w21a the hand does not animate.

Not invalid - you can get below 0 in any void-based world by water ladder. I believe water is allowed in The End, which is a void world.
And of course, the ever-popular Skyblock style worlds basically require this water ladder technique, which could easily lead to players attempting to build below 0.

Can confirm for 1.13-pre4.

In 1.16.1 and 20w27a
Can confirm in 20w51a.
Can confirm in 21w05b.
Added "vanilla-parity" tags since Bedrock Edition's latest beta has added these error/warning messages.
[media]

Can confirm in 1.17.1.

Can confirm in 21w44a.

can confirm in 1.20 Pre-release 1.

Code Analysis:
The following is based on a decompiled version of Minecraft 1.20.1 using MCP-Reborn.
net.minecraft.server.network.ServerGamePacketListenerImpl.java // Original
public class ServerGamePacketListenerImpl implements ServerPlayerConnection, TickablePacketListener, ServerGamePacketListener {
...
public void handleUseItemOn(ServerboundUseItemOnPacket p_9930_) {
PacketUtils.ensureRunningOnSameThread(p_9930_, this, this.player.serverLevel());
this.player.connection.ackBlockChangesUpTo(p_9930_.getSequence());
ServerLevel serverlevel = this.player.serverLevel();
InteractionHand interactionhand = p_9930_.getHand();
ItemStack itemstack = this.player.getItemInHand(interactionhand);
if (itemstack.isItemEnabled(serverlevel.enabledFeatures())) {
BlockHitResult blockhitresult = p_9930_.getHitResult();
Vec3 vec3 = blockhitresult.getLocation();
BlockPos blockpos = blockhitresult.getBlockPos();
Vec3 vec31 = Vec3.atCenterOf(blockpos);
if (!(this.player.getEyePosition().distanceToSqr(vec31) > MAX_INTERACTION_DISTANCE)) {
Vec3 vec32 = vec3.subtract(vec31);
double d0 = 1.0000001 D;
if (Math.abs(vec32.x()) < 1.0000001 D && Math.abs(vec32.y()) < 1.0000001 D && Math.abs(vec32.z()) < 1.0000001 D) {
Direction direction = blockhitresult.getDirection();
this.player.resetLastActionTime();
int i = this.player.level().getMaxBuildHeight();
if (blockpos.getY() < i) {
if (this.awaitingPositionFromClient == null && this.player.distanceToSqr((double) blockpos.getX() + 0.5 D, (double) blockpos.getY() + 0.5 D, (double) blockpos.getZ() + 0.5 D) < 64.0 D && serverlevel.mayInteract(this.player, blockpos)) {
InteractionResult interactionresult = this.player.gameMode.useItemOn(this.player, serverlevel, itemstack, interactionhand, blockhitresult);
if (direction == Direction.UP && !interactionresult.consumesAction() && blockpos.getY() >= i - 1 && wasBlockPlacementAttempt(this.player, itemstack)) {
Component component = Component.translatable("build.tooHigh", i - 1).withStyle(ChatFormatting.RED);
this.player.sendSystemMessage(component, true);
} else if (interactionresult.shouldSwing()) {
this.player.swing(interactionhand, true);
}
}
} else {
Component component1 = Component.translatable("build.tooHigh", i - 1).withStyle(ChatFormatting.RED);
this.player.sendSystemMessage(component1, true);
} this.player.connection.send(new ClientboundBlockUpdatePacket(serverlevel, blockpos));
this.player.connection.send(new ClientboundBlockUpdatePacket(serverlevel, blockpos.relative(direction)));
} else {
LOGGER.warn("Rejecting UseItemOnPacket from {}: Location {} too far away from hit block {}.", this.player.getGameProfile().getName(), vec3, blockpos);
}
}
}
}
...
}
As seen in the class ServerGamePacketListenerImpl
, we see that there's only functionality for the max height warning indicator, not the min world height.
Fix:
To fix this, all we have to do is to add compatibility for min world height. Really, it's just copy and pasting with a couple more modifications.
net.minecraft.server.network.ServerGamePacketListenerImpl.java // Updated
public class ServerGamePacketListenerImpl implements ServerPlayerConnection, TickablePacketListener, ServerGamePacketListener {
...
public void handleUseItemOn(ServerboundUseItemOnPacket p_9930_) {
PacketUtils.ensureRunningOnSameThread(p_9930_, this, this.player.serverLevel());
this.player.connection.ackBlockChangesUpTo(p_9930_.getSequence());
ServerLevel serverlevel = this.player.serverLevel();
InteractionHand interactionhand = p_9930_.getHand();
ItemStack itemstack = this.player.getItemInHand(interactionhand);
if (itemstack.isItemEnabled(serverlevel.enabledFeatures())) {
BlockHitResult blockhitresult = p_9930_.getHitResult();
Vec3 vec3 = blockhitresult.getLocation();
BlockPos blockpos = blockhitresult.getBlockPos();
Vec3 vec31 = Vec3.atCenterOf(blockpos);
if (!(this.player.getEyePosition().distanceToSqr(vec31) > MAX_INTERACTION_DISTANCE)) {
Vec3 vec32 = vec3.subtract(vec31);
double d0 = 1.0000001 D;
if (Math.abs(vec32.x()) < 1.0000001 D && Math.abs(vec32.y()) < 1.0000001 D && Math.abs(vec32.z()) < 1.0000001 D) {
Direction direction = blockhitresult.getDirection();
this.player.resetLastActionTime();
int i = this.player.level().getMaxBuildHeight();
int j = this.player.level().getMinBuildHeight(); if (blockpos.getY() < i && blockpos.getY() >= j) {
if (this.awaitingPositionFromClient == null && this.player.distanceToSqr((double) blockpos.getX() + 0.5 D, (double) blockpos.getY() + 0.5 D, (double) blockpos.getZ() + 0.5 D) < 64.0 D && serverlevel.mayInteract(this.player, blockpos)) {
InteractionResult interactionresult = this.player.gameMode.useItemOn(this.player, serverlevel, itemstack, interactionhand, blockhitresult);
if (direction == Direction.UP && !interactionresult.consumesAction() && blockpos.getY() >= i - 1 && wasBlockPlacementAttempt(this.player, itemstack)) {
Component component = Component.translatable("build.tooHigh", i - 1).withStyle(ChatFormatting.RED);
this.player.sendSystemMessage(component, true);
} else if (direction == Direction.DOWN && !interactionresult.consumesAction() && blockpos.getY() <= j && wasBlockPlacementAttempt(this.player, itemstack)) {
Component component = Component.translatable("build.tooLower", j + 1).withStyle(ChatFormatting.RED);
this.player.sendSystemMessage(component, true);
} else if (interactionresult.shouldSwing()) {
this.player.swing(interactionhand, true);
}
}
} else {
if (direction == Direction.UP) {
Component component1 = Component.translatable("build.tooHigh", i - 1).withStyle(ChatFormatting.RED);
this.player.sendSystemMessage(component1, true);
} else if (direction == Direction.DOWN) {
Component component1 = Component.translatable("build.tooLower", j + 1).withStyle(ChatFormatting.RED);
this.player.sendSystemMessage(component1, true);
}
} this.player.connection.send(new ClientboundBlockUpdatePacket(serverlevel, blockpos));
this.player.connection.send(new ClientboundBlockUpdatePacket(serverlevel, blockpos.relative(direction)));
} else {
LOGGER.warn("Rejecting UseItemOnPacket from {}: Location {} too far away from hit block {}.", this.player.getGameProfile().getName(), vec3, blockpos);
}
}
}
}
...
}
A fair warning though, this may be a solution to indicate a lower build limit warning, you will need to add a translatable to en_us.json
for the key: build.toLower.
As an example:
"build.tooLower": "Height limit for building is %s",