mojira.dev
MC-104259

Player / mob stuck on farmland while it changes to dirt

The bug

Since farmland is now 15/16 of a block, jumping on it will change it back to dirt, which is a full block. The player is now stuck inside the block.

Caused by fixing MC-85162.

Code analysis / Fix

See this comment by @unknown.

Linked issues

Attachments

Comments 24

Caused by the fix of MC-85162 ?

Can confirm.

PencilSharpener

You also get stuck if farmland reverts to dirt naturally while you're standing on it.

I didn't see this issue before submitting mine, but that's because I was looking for the keyword villager in the subject and should have searched for farmland or similar. Anyways, on mine, I attached a video showing a villager falling through the block. See MC-104906. Hope it helps get it resolved soon.

Daniel Burnett

Confirmed for 1.10.2 as well. The obvious fix would be that when the farmland converts to dirt, it finds all entities within its new bounding box (or within its old bounding box + 1/16 on the y axis) and teleports them up by 0.0625 (1/16) blocks. From what I remember of modding, that should be just a few lines of code to fix this (of course, I'm not sure how different the Forge API is to the original source code, so maybe it's harder than it seems).

14 more comments
Timothy Miller

I've had this happen even WITHOUT the block turning back to dirt.

Can anyone still reproduce this with a single farmland block only or only with at least two (see this comment)? If not the description needs to be updated.

I could directly reproduce it with the setup described in the post you linked (sign on 2nd farmland etc.), couldn't reproduce with single farmland yet, but maybe my setup for one farmland is wrong atm.
Also can't reproduce it so far with the single-farmland-instructions from the same comment (positioning at .9).

I've only been able to reproduce this (and have only been testing with) the 2-block variant.

Alright, figured this out. (MCP 940, with 1.12.1 SRG mappings)

Why it only works some of the time

@unknown' fix in 16w35a (which was suggested by @unknown) is correct, but the implementation has a slight flaw: it only sets the position on the server; the client isn't informed that it should move up. However, there is another mechanism that later causes the client to be informed: the serverside check that prevents players from moving into blocks. When the block is changed, the client starts falling into it. However, the server detects this and rejects that movement (after all, the server thinks the player is on top of the block). The server then teleports the player on top of the block, and everything is fine.

However, if the player does not fall (i.e. they're partially standing on another block), then the server doesn't move them up (even though they are still positioned inside a block). Why doesn't the server think they're in a block and thus move them out in this case? Well, for some reason, hitboxes are shrunk slightly before checking if the player is in a block. Specifically, they're shrunk by 0.0625D... or precisely 1/16th of a block. So the server is completely fine with the player being partially inside the farmland. And thus, the player can still move around, and overwrite the moved up position that the farmland chose. Then, when the player moves too far forward and has no other block holding them up, they start falling into the farmland. This time, there's no server position outside of the block to help them, and they get stuck.

Fix

To fix this, the client needs to be informed of the change in position using NetHandlerPlayClient.setPlayerLocation (this same special casing is used in plenty of other places, including the teleport commands):

private void turnToDirt(World worldIn, BlockPos pos) {
    IBlockState iblockstate = Blocks.DIRT.getDefaultState();
    worldIn.setBlockState(pos, iblockstate);
    AxisAlignedBB axisalignedbb = iblockstate.getCollisionBoundingBox(worldIn, pos).offset(pos);

    for (Entity entity : worldIn.getEntitiesWithinAABBExcludingEntity((Entity)null, axisalignedbb)) {
        if (entity instanceof EntityPlayerMP) {
            double delta = axisalignedbb.maxY - entity.posY;
            ((EntityPlayerMP) entity).connection.setPlayerLocation(0, delta, 0, 0, 0, EnumSet.allOf(SPacketPlayerPosLook.EnumFlags.class));
        } else {
            entity.setPosition(entity.posX, axisalignedbb.maxY, entity.posZ);
        }
    }
}

(I use a relative teleport instead of an absolute one because a relative teleport will be smoother)

This same logic should be added to BlockGrassPath. While grass paths can't be trampled, they do change when blocks are placed above them (for instance, signs). They do not make any attempt to move players out of the ground, which means that players will get stuck in them when the block is placed (again, the 1/16th of a block contraction). Fixing this simply requires adding the same code to BlockGrassPath's dirt conversion logic (which MCP calls updateBlockState).

purpleturtle88

migrated

Confirmed

dirt, farmland, mob, player

Minecraft 1.10.1, Minecraft 1.10.2, Minecraft 16w32a, Minecraft 16w32b, Minecraft 16w33a, ..., Minecraft 1.12, Minecraft 17w31a, Minecraft 1.12.1 Pre-Release 1, Minecraft 1.12.1, Minecraft 1.12.2 Pre-Release 1

Minecraft 16w35a, Minecraft 1.12.2 Pre-Release 2

Retrieved