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.
Linked issues
is duplicated by 2
relates to 2
Attachments
Comments 12
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.
Added "vanilla-parity" tags since Bedrock Edition's latest beta has added these error/warning messages.
[media]
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",
Invalid, as you usually can't get there. But in creative you can, so it's up to mojang to decide.