mojira.dev
MC-129886

Trying to place a block underneath min_y does not give an error message

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

Attachments

Comments

migrated
[media][media][media]
CreeperMagnet_

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

muzikbike

As of 18w21a the hand does not animate.

migrated

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.

CreeperMagnet_

Can confirm for 1.13-pre4.

pulpetti

In 1.16.1 and 20w27a

Avoma

Can confirm in 20w51a.

Avoma

Can confirm in 21w05b.

lillybeacon

Added "vanilla-parity" tags since Bedrock Edition's latest beta has added these error/warning messages.

[media]

ampolive

Can confirm in 1.17.1.

shufboyardee

Can confirm in 21w44a.

shufboyardee

can confirm in 1.20 Pre-release 1.

Apollo30

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",

muzikbike

(Unassigned)

Confirmed

Platform

Low

UI

build-limit, ui, vanilla-parity, void, y-coordinate

Minecraft 1.12.2, Minecraft 18w20c, Minecraft 18w21a, Minecraft 18w21b, Minecraft 18w22a, ..., 1.19.4, 23w16a, 1.20 Pre-release 1, 1.20.1, 1.20.4

Retrieved