Using precise alignments, it is possible for players to clip into the collision box of any block that isn’t fully solid (does not have full 1x1x1 block collision).
How to reproduce (see attached video clip.mp4):
- Stand right next to any non-fully solid block with the block such that you are facing towards X+ when looking at the block.
- Enter the command: /tp @s ~-0.43023130200150505 ~ ~ -90 ~
- Start walking forward without sprinting until you should successfully clip into the block.
(This very rarely doesn’t work if the x-coordinate of the block equals a power of 2, make sure this isn’t the case)
Code Analysis:
This bug is caused by the 1.0E-7 lenience of the IndirectMerger class. The IndirectMerger class is meant to take two double lists as parameters and produce a new double list, which represents the combined VoxelShape of the intersection of the two shapes (e.g. [0.2, 0.6, 0.9] and [0.0, 0.4, 0.7] → [0.2, 0.4, 0.6, 0.7]). Suppose for simplicity both lists only contain two doubles (this is in fact the case for most block/entity VoxelShapes), then in our provided reproduction setup, the code tries merging two double lists similar to [1.0, 2.0] and [0.2, 1.0 + 1.0E-8]. Instead of merging these lists to [1.0, 1.0 + 1.0E-8, 2.0], the code combines any two consecutive doubles with a distance of < 1.0E-7 as seen in this snippet:
if (!(lastValue >= nextValue - 1.0E-7)) {
this.firstIndices[resultIndex] = currentFirstIndex;
this.secondIndices[resultIndex] = currentSecondIndex;
this.result[resultIndex] = nextValue;
resultIndex++;
lastValue = nextValue;
} else {
this.firstIndices[resultIndex - 1] = currentFirstIndex;
this.secondIndices[resultIndex - 1] = currentSecondIndex;
}
Thus the small intersection of [1.0, 1.0 + 1.0E-8] is ignored, allowing the player to clip into the block.
To ensure that the merged list remains fully ordered, one would also have to remove the first 1.0E-7 check at
boolean choseFirst = !ranOutOfFirst && (ranOutOfSecond || first.getDouble(firstIndex) < second.getDouble(secondIndex) + 1.0E-7);
The reason this doesn’t occur at all for fully solid blocks, is the following case distinction in BlockCollisions::computeNext:
if (blockShape == Shapes.block()) {
if (this.box.intersects(x, y, z, x + 1.0, y + 1.0, z + 1.0)) {
return (T)this.resultProvider.apply(this.pos, blockShape.move(this.pos));
}
} else {
VoxelShape shape = blockShape.move(this.pos);
if (!shape.isEmpty() && Shapes.joinIsNotEmpty(shape, this.entityShape, BooleanOp.AND)) {
return (T)this.resultProvider.apply(this.pos, shape);
}
}Attachments
Comments 2
Thank you for your report!
After consideration, the issue is being closed as Won't Fix.
Please note that this is not the same as Working as Intended, as this bug report correctly describes behavior in the game that might not be the intended or desirable behaviour, but it will not be fixed right now. Sometimes, this is because the issue reported is minor and/or impossible to change without large architectural changes to the code base.
If you'd like to post a suggestion or give further feedback, please visit the Minecraft Feedback Site or choose a link below.
Quick Links:
📓 Issue Guidelines – 💬 Mojang Support – 📧 Suggestions – 📖 Minecraft Wiki
Thank you for helping us improve Minecraft! We saved your files: