0% found this document useful (0 votes)
16 views

Glitch_ placing block on non-visible side

The document discusses a glitch in Minecraft that allows players to place blocks on non-visible sides of blocks, which can be exploited for various gameplay strategies. It details the mechanics of the glitch, potential applications, and ongoing research into its underlying causes, including raycasting issues in the game's code. The author expresses a desire to further investigate the glitch's behavior and its implications for gameplay.

Uploaded by

rohanshri66
Copyright
© © All Rights Reserved
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
16 views

Glitch_ placing block on non-visible side

The document discusses a glitch in Minecraft that allows players to place blocks on non-visible sides of blocks, which can be exploited for various gameplay strategies. It details the mechanics of the glitch, potential applications, and ongoing research into its underlying causes, including raycasting issues in the game's code. The author expresses a desire to further investigate the glitch's behavior and its implications for gameplay.

Uploaded by

rohanshri66
Copyright
© © All Rights Reserved
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 11

BUG: placing block on non-visible side

This is a glitch which allows you to place blocks on the face where you
are not supposed to be able to look at. (e.g. placing a new block on the
opposite side of the block you are looking at)

(works between 1.0 - 1.12)

Applications: ​
1. bridge/pillar downward​
2. no looking back sprint(jump) bridge (ie. bedrock-like bridging)​
3. other case that you’ll benefit from placing block where you shouldn’t be able to reach(but
the physical distance in within the reach)

2025/01/15 Update: This is not supposed to be an explanation doc but one for
research purposes, I was making a programming project so I procrastinate to study
this glitch since 3 months ago.

(Research hasn’t yet done so this is actually not intended to be public, but I’ll
continue the research soon. Please do proper credit if you’re make a video out from
here)

So the current theory of how this glitch work is:


when the game tries to do raycast to check which block face it is hitting, the ray is
way too parallel and close to the block that it nearly cause zero division error, but the
minecraft code prevents it (​it is another e-7 stuff), making it ignore the side-hit of
raycast, reaching the non-visible side of the block.

First known occurrence of the glitch(by Crimssel in 6/2):


https://youtu.be/s_wOhU3p-ho?si=2i1tFv8xer689CQb​
First replicate in single-player(by Virox in 10/26):
(youtube reupload if discord link doesn’t work)​

I found a setup in survival, pitch lenient, and was able to place block on the complete
opposite face​

https://go.screenpal.com/watch/cZ6tXgncIP3

for pitch 0, z = 1, angle = 360/65536 deg (1 sig angle), the viable x coord will range from
0.000191748 ~ 0.0002876214
which turns out to be equal to position range that you’ll be looking on the left(pos x) side of
the block :​

(3-1)*tan(2*pi*1/65536) and (4-1)*tan(2*pi*1/65536)

(ignore)
a weird thing I haven’t figure out is when have pitch 13 instead of 0, the range became
0.000191748 ~ 0.0002575299
Does not make much sense to me, a line parallel to y axis should be pitch constant
(edit: the ray moves out the bottom left edge of the block first, I’m dumb)
--------------------
Virox found something interesting - no looking back sprint jump bridge
(discord link doesn’t work: https://youtu.be/2vdWZH0RyBs?si=OxSRUKXcjWzk0fYu )

Sprinting forward without jumping is super easy with just spamming non-double click
butterfly https://go.screenpal.com/watch/cZ6tl1ncI9x

This works because we are in luck, we have a non-zero angle that does not move our x
coordinate when moving along the z axis.
This is an half angle (in this case, 0.00549 is the only numerical value within the same
decimal precision, so precise)
0.00549° Value Angle Index
Sin 0 0 0
Cos 1 0.005493 1 (16385)
Normal 1 (from mothball - a parkour movement simulator)

The sine part of the angle is working as it intended(floored down to 0), but the cosine part is
calculated with sine(90 deg - theAngle) and because of float point issue, it gets the wrong
value from the sine table. So here we have a non zero angle that acts like a zero on the x
axis. – further reading
---------------
The application is cool and fun, but what’s more important is to find why the glitch works –
down to the source code level preferably, but before looking at code I want to experiment
with the behavior of the glitch a little more.
I suppose aiming to slightly left to the “rib” of the block is always in the working range (if it
work at all), I made a quick code that generates tp commands for me when I input sig_angle

first attempt:
I had arguments set as default, zDistance = 2(to the front face of block), pitch 0, trying to
place the block at the back side. As a result, sig angle index from 1-63, only angle 1
works(being able to place blocks on the left side means not working). Which was
embarrassing. But clearly in virox’s clip he uses a different angle from index 1 (index 18) and
it still works.

Then I realize another factor: distance to the block

Still I don’t know the exact reason, but further means wide angle range accepted from
experimentation. By making distance 2 -> 4, the possible angle-index range had increased to
1-3. Note that I wasn’t able to increase the distance more, because the whole time, I was
aiming at the back side of the block, the actual reach is 5, the maximum in creative. (angle
1-3 works in survival reach thankfully btw)

I claimed the only positive significant angles that could place block at the opposite
side is index 1-3
To extend the reach, we could try placing blocks at the bottom or top place which is also a
part of the glitch. That extends the reach by a block if we are aiming at the exact corner
closest to use on the left face. Quickly, I was able to get index 4 working. With more testing,
the angle range had become 1 - 475 (up to 2.60925292969 deg !)

https://go.screenpal.com/watch/cZ6tInncIWq

I also found factors other than distance, where you aim matters too. Look at this example:

/tp 0.02446780182631895 12 -1.4 0.318603515625 8.0 (index58) works


/tp 0.02488966940106573 12 -1.4 0.3240966796875 8.0 (index59) did not (placed on the
side instead)
but pitch 8.0 -> 8.02 works again, so, aiming at the most corner possible?

475 works in survival, but 476, whatever I tried: creative mode, making the target block lower
to try out pitches, squeezing out more distance, it doesn't work, and that’s pretty strange.

----------------
Then I go looked at 1.8.9 minecraft’s code (the first time I do so lol, for this), I extract out the
possible related code that controls block-select/ ray-tracing

uncompiled source code github


(I renamed some variables, the original is completely unreadable)
(from world.World) determines what block and side you are looking at
public MovingObjectPosition rayTraceBlocks(Vec3 rayOrigin, Vec3 rayTarget,
boolean stopOnLiquid, boolean ignoreBlockWithoutBoundingBox, boolean
returnLastNonCollidableBlock)
{
if (!Double.isNaN(rayOrigin.xCoord) && !Double.isNaN(rayOrigin.yCoord) &&
!Double.isNaN(rayOrigin.zCoord) && !Double.isNaN(rayTarget.xCoord) &&
!Double.isNaN(rayTarget.yCoord) && !Double.isNaN(rayTarget.zCoord))
{

int targetX = MathHelper.floor_double(rayTarget.xCoord);


int targetY = MathHelper.floor_double(rayTarget.yCoord);
int targetZ = MathHelper.floor_double(rayTarget.zCoord);
int startX = MathHelper.floor_double(rayOrigin.xCoord);
int startY = MathHelper.floor_double(rayOrigin.yCoord);
int startZ = MathHelper.floor_double(rayOrigin.zCoord);
BlockPos blockpos = new BlockPos(startX, startY, startZ);
IBlockState iblockstate = this.getBlockState(blockpos);
Block block = iblockstate.getBlock();
if ((!ignoreBlockWithoutBoundingBox ||
block.getCollisionBoundingBox(this, blockpos, iblockstate) != null) &&
block.canCollideCheck(iblockstate, stopOnLiquid))
{
MovingObjectPosition movingobjectposition =
block.collisionRayTrace(this, blockpos, rayOrigin, rayTarget);

if (movingobjectposition != null)
{
return movingobjectposition;
}
}

MovingObjectPosition movingobjectposition2 = null;


int iterationsLeft = 200;

while (iterationsLeft-- >= 0)


{
if (Double.isNaN(rayOrigin.xCoord) ||
Double.isNaN(rayOrigin.yCoord) || Double.isNaN(rayOrigin.zCoord))
{
return null;
}

if (startX == targetX && startY == targetY && startZ == targetZ)


{
return returnLastNonCollidableBlock ? movingobjectposition2 :
null;
}

boolean flagAboutX = true;


boolean flagAboutY = true;
boolean flagAboutZ = true;
double checkX = 999.0D;
double checkY = 999.0D;
double checkZ = 999.0D;

if (targetX > startX)


{
checkX = (double)startX + 1.0D;
}
else if (targetX < startX)
{
checkX = (double)startX + 0.0D;
}
else
{
flagAboutX = false;
}

if (targetY > startY)


{
checkY = (double)startY + 1.0D;
}
else if (targetY < startY)
{
checkY = (double)startY + 0.0D;
}
else
{
flagAboutY = false;
}

if (targetZ > startZ)


{
checkZ = (double)startZ + 1.0D;
}
else if (targetZ < startZ)
{
checkZ = (double)startZ + 0.0D;
}
else
{
flagAboutZ = false;
}

double deltaX = 999.0D;


double deltaY = 999.0D;
double deltaZ = 999.0D;
double rayDX = rayTarget.xCoord - rayOrigin.xCoord;
double rayDY = rayTarget.yCoord - rayOrigin.yCoord;
double rayDZ = rayTarget.zCoord - rayOrigin.zCoord;

if (flagAboutX)
{
deltaX = (checkX - rayOrigin.xCoord) / rayDX;
}

if (flagAboutY)
{
deltaY = (checkY - rayOrigin.yCoord) / rayDY;
}

if (flagAboutZ)
{
deltaZ = (checkZ - rayOrigin.zCoord) / rayDZ;
}

if (deltaX == -0.0D)
{
deltaX = -1.0E-4D;
}

if (deltaY == -0.0D)
{
deltaY = -1.0E-4D;
}

if (deltaZ == -0.0D)
{
deltaZ = -1.0E-4D;
}

EnumFacing hitSide;

if (deltaX < deltaY && deltaX < deltaZ) //deltaX smallest, If


deltaX is smallest, it means that moving the ray along the X-axis will reach
the next voxel boundary before moving along Y or Z.
{
hitSide = targetX > startX ? EnumFacing.WEST :
EnumFacing.EAST;
rayOrigin = new Vec3(checkX, rayOrigin.yCoord + rayDY *
deltaX, rayOrigin.zCoord + rayDZ * deltaX);
}
else if (deltaY < deltaZ) //deltaY smallest
{
hitSide = targetY > startY ? EnumFacing.DOWN : EnumFacing.UP;
rayOrigin = new Vec3(rayOrigin.xCoord + rayDX * deltaY,
checkY, rayOrigin.zCoord + rayDZ * deltaY);
}
else //deltaZ smallest
{
hitSide = targetZ > startZ ? EnumFacing.NORTH :
EnumFacing.SOUTH;
rayOrigin = new Vec3(rayOrigin.xCoord + rayDX * deltaZ,
rayOrigin.yCoord + rayDY * deltaZ, checkZ);
}

startX = MathHelper.floor_double(rayOrigin.xCoord) - (hitSide ==


EnumFacing.EAST ? 1 : 0);
startY = MathHelper.floor_double(rayOrigin.yCoord) - (hitSide ==
EnumFacing.UP ? 1 : 0);
startZ = MathHelper.floor_double(rayOrigin.zCoord) - (hitSide ==
EnumFacing.SOUTH ? 1 : 0);
blockpos = new BlockPos(startX, startY, startZ);
IBlockState iblockstate1 = this.getBlockState(blockpos);
Block block1 = iblockstate1.getBlock();

if (!ignoreBlockWithoutBoundingBox ||
block1.getCollisionBoundingBox(this, blockpos, iblockstate1) != null)
{
if (block1.canCollideCheck(iblockstate1, stopOnLiquid))
{
MovingObjectPosition movingobjectposition1 =
block1.collisionRayTrace(this, blockpos, rayOrigin, rayTarget);

if (movingobjectposition1 != null)
{
return movingobjectposition1;
}
}
else
{
movingobjectposition2 = new
MovingObjectPosition(MovingObjectPosition.MovingObjectType.MISS, rayOrigin,
hitSide, blockpos);
}
}

}
return returnLastNonCollidableBlock ? movingobjectposition2 : null;
}
else
{
return null;
}
}

the Block.collisionRayTrace is linked here:


(from block.Block)
public MovingObjectPosition collisionRayTrace(World world, BlockPos
blockPosition, Vec3 rayStart, Vec3 rayEnd) {
// Set block boundaries based on its state in the world
this.setBlockBoundsBasedOnState(world, blockPosition);

// Move ray start and end points to local block coordinates


Vec3 localRayStart = rayStart.addVector(-blockPosition.getX(),
-blockPosition.getY(), -blockPosition.getZ());
Vec3 localRayEnd = rayEnd.addVector(-blockPosition.getX(),
-blockPosition.getY(), -blockPosition.getZ());

// Calculate intersection points with each face of the block


Vec3 intersectMinX = localRayStart.getIntermediateWithXValue(localRayEnd,
this.minX);
Vec3 intersectMaxX = localRayStart.getIntermediateWithXValue(localRayEnd,
this.maxX);
Vec3 intersectMinY = localRayStart.getIntermediateWithYValue(localRayEnd,
this.minY);
Vec3 intersectMaxY = localRayStart.getIntermediateWithYValue(localRayEnd,
this.maxY);
Vec3 intersectMinZ = localRayStart.getIntermediateWithZValue(localRayEnd,
this.minZ);
Vec3 intersectMaxZ = localRayStart.getIntermediateWithZValue(localRayEnd,
this.maxZ);

// Validate intersections to ensure they are within the bounds of the


block's faces
if (!this.isVecInsideYZBounds(intersectMinX)) {
intersectMinX = null;
}
if (!this.isVecInsideYZBounds(intersectMaxX)) {
intersectMaxX = null;
}
if (!this.isVecInsideXZBounds(intersectMinY)) {
intersectMinY = null;
}
if (!this.isVecInsideXZBounds(intersectMaxY)) {
intersectMaxY = null;
}
if (!this.isVecInsideXYBounds(intersectMinZ)) {
intersectMinZ = null;
}
if (!this.isVecInsideXYBounds(intersectMaxZ)) {
intersectMaxZ = null;
}

// Determine the closest intersection point along the ray


Vec3 closestIntersection = null;

if (intersectMinX != null && (closestIntersection == null ||


localRayStart.squareDistanceTo(intersectMinX) <
localRayStart.squareDistanceTo(closestIntersection))) {
closestIntersection = intersectMinX;
}
if (intersectMaxX != null && (closestIntersection == null ||
localRayStart.squareDistanceTo(intersectMaxX) <
localRayStart.squareDistanceTo(closestIntersection))) {
closestIntersection = intersectMaxX;
}
if (intersectMinY != null && (closestIntersection == null ||
localRayStart.squareDistanceTo(intersectMinY) <
localRayStart.squareDistanceTo(closestIntersection))) {
closestIntersection = intersectMinY;
}
if (intersectMaxY != null && (closestIntersection == null ||
localRayStart.squareDistanceTo(intersectMaxY) <
localRayStart.squareDistanceTo(closestIntersection))) {
closestIntersection = intersectMaxY;
}
if (intersectMinZ != null && (closestIntersection == null ||
localRayStart.squareDistanceTo(intersectMinZ) <
localRayStart.squareDistanceTo(closestIntersection))) {
closestIntersection = intersectMinZ;
}
if (intersectMaxZ != null && (closestIntersection == null ||
localRayStart.squareDistanceTo(intersectMaxZ) <
localRayStart.squareDistanceTo(closestIntersection))) {
closestIntersection = intersectMaxZ;
}

// If no intersection found, return null


if (closestIntersection == null) {
return null;
} else {
// Determine the face of the block that was hit
EnumFacing hitFace = null;

if (closestIntersection == intersectMinX) {
hitFace = EnumFacing.WEST;
}
if (closestIntersection == intersectMaxX) {
hitFace = EnumFacing.EAST;
}
if (closestIntersection == intersectMinY) {
hitFace = EnumFacing.DOWN;
}
if (closestIntersection == intersectMaxY) {
hitFace = EnumFacing.UP;
}
if (closestIntersection == intersectMinZ) {
hitFace = EnumFacing.NORTH;
}
if (closestIntersection == intersectMaxZ) {
hitFace = EnumFacing.SOUTH;
}

// Return the intersection result with the world coordinates restored


return new
MovingObjectPosition(closestIntersection.addVector(blockPosition.getX(),
blockPosition.getY(), blockPosition.getZ()), hitFace, blockPosition);
}
}

I found out getIntermediateWithXValue is a minecraft internal codebase vec3 class method

public Vec3 getIntermediateWithXValue(Vec3 vec, double x) {


double d0 = vec.xCoord - this.xCoord; // Difference in X-coordinates
double d1 = vec.yCoord - this.yCoord; // Difference in Y-coordinates
double d2 = vec.zCoord - this.zCoord; // Difference in Z-coordinates

// Check for near-zero delta in X to avoid division by zero


if (d0 * d0 < 1.0000000116860974E-7D) {
return null; // No intersection if the ray is nearly parallel to the X plane
} else {
double d3 = (x - this.xCoord) / d0; // Calculate the ratio of intersection
// Check if the intersection point lies within the segment defined by the two Vec3 points
return d3 >= 0.0D && d3 <= 1.0D ? new Vec3(this.xCoord + d0 * d3, this.yCoord + d1 *
d3, this.zCoord + d2 * d3) : null;
}
}

if the supposed ray collide facing is not detected(be set null), then it’ll be ignored.
if (d0 * d0 < 1.0000000116860974E-7D) {
return null; // No intersection if the ray is nearly parallel to the X plane

You might also like