mojira.dev
MC-302136

When changing to standing from sleeping, mannequins fall one block

Do the following, in a way that the mannequin is summoned well above the ground:

/summon mannequin ^ ^ ^3 {NoGravity:True}
/data modify entity @n[type=minecraft:mannequin] pose set value sleeping
/data modify entity @n[type=minecraft:mannequin] pose set value standing

You will notice that the mannequin is left standing one block lower than it was summoned.

Attachments

Comments 2

This affects the latest release, 1.21.11.


Analysis:

A possible cause of this bug can be attributed to the refreshDimensions method within Entity.class. A mannequin has a height of 0.2 blocks when sleeping, and a height of 1.8 blocks while standing. When an entity’s size or dimensions change, the fudgePositionAfterSizeChange(EntityDimensions previousDimensions) is what handles the changes.

When switching from a sleeping to a standing position, the fudgePositionAfterSizeChange method gets the entity’s center based on its current (sleeping) dimensions:

Snippet 1

Vec3 oldCenter = this.position().add((double)0.0F, (double)previousDimensions.height() / (double)2.0F, (double)0.0F);

Once again, the height when sleeping is 0.2 blocks, so oldCenter becomes currentPosition.y + 0.1.

Lower down within the Method, a free space for the new (standing) dimensions around the new center is checked for:

Snippet 2

if (freePosition.isPresent()) {
            this.setPos(((Vec3)freePosition.get()).add((double)0.0F, (double)(-newDimensions.height()) / (double)2.0F, (double)0.0F));
            return true;
        }

when the mannequin is standing again, newDimensions.height() is 1.8m, and the new Y position will become oldCenter - 0.9, which is (currentPosition.y + 0.1) - 0.9 = currentPosition.y - 0.8.


A mathematical explanation with an example:

Let’s say a mannequin starts at Y = 100.0.

We know:

Sleeping height = 0.2m

Standing height = 1.8m

Plugging in our values into a simplified version of Snippet 1 from above:

Vec3 oldCenter = this.position().add(0.0, (double)previousDimensions.height() / 2.0, 0.0);

this.position().y = 100.0

previousDimensions.height() / 2.0 = 0.2 / 2.0 = 0.1

oldCenter = 100.1 (what the game thinks the middle of the mannequin is at)

The game then attempts to look for a place to put the new standing mannequin while trying to keep the center of the new positioned mannequin as close to oldCenter as possible. Because of the freePosition check from earlier, nothing is in the way, so freePosition is 100.1 and returns true… for a clear position.

Now the game must convert that same center point or freePosition back into a ‘feet’ position, or where the actual Y coordinate of an entity is measured, it subtracts half of the new height, with new code from a simplified Snippet 2 from above:

this.setPos(((Vec3) freePosition.get()).add(0.0, -newDimensions.height() / 2.0, 0.0));

Plugging in our values:

freePosition.y = 100.1

-newDimensions.height() / 2.0 = -1.8 / 2.0 = -0.9

100.1 - 0.9 = 99.2, a difference of 0.8 blocks from our initial position of Y = 100.0

This means that the mannequin’s Y position gets shifted down by 0.8 blocks (as proven in the video below)


Possible solutions and considerations:

The Entity.refreshDimensions method has a check to exclude the Player:

if (... && !(this instanceof Player)) {
            this.fudgePositionAfterSizeChange(oldDim);
        }

A recommended fix is to have an override for the fudgePositionAfterSizeChange method within the Mannequin class to return false without changing the entity’s position.

// Possible fix
@Override
public boolean fudgePositionAfterSizeChange(EntityDimensions previousDimensions) {
    return false;
}

As mannequins are not traditional entities like the player or other mobs, and are data driven entities meant to be controlled with commands, their position can be expected to remain the same across different states or size/dimension changes, this feels like an appropriate change for their behavior.

kcrca

(Unassigned)

Community Consensus

Platform

Normal

Commands, Entities

1.21.9 Pre-Release 1, 1.21.11

Retrieved