mojira.dev
MC-10176

Oak/Dark Oak Tree Branches can replace all solid blocks (including Bedrock and Barrier)

To reproduce:

1 Build "tube" of any solid block as high as you want, with "open edges"

2 Plant sapling and grow it with bone meal.

3 Remove leaves to see, where wood replaced the solid blocks of the tube.

I suppose the kind of the solid block does not matter.

Linked issues

MC-39802 Dark oak saplings can break BEDROCK Resolved MC-39994 Roofed Oak Tree grwoth can destroy blocks under the saplings Resolved MC-40091 Dark Oak roots can break bedrock on top of the Nether (after enderpearling up there) Resolved MC-40289 Bed rock breaking Resolved MC-40408 Dark Oak Position Bug Resolved

Attachments

Comments 26

Confirmed.

The branches can also destroy bedrock!

Voting/

Michael Schiffner

Looks everytime like this with closed corners/edges.

Two bugs in the play.

Reason 1
At least for big (oak) tree generation, the method that checks whether a line of blocks is ok for generation has half-a-block difference compared to the method that later places the blocks. Thus, with (un)suitable angles, the checker slips through the corner, while the placer ends up stepping through the solid block.

Fix 1
Add the missing +0.5D's on the two minor axis. Using MCP and own namings, but should be clear where it is.

WorldGenBigTree.checkBlockLine()

...
    currentSpot[largestEndDeltaAxis] = from[largestEndDeltaAxis] + blockStep;
    currentSpot[axis2] = MathHelper.floor_double((double) from[axis2] + (double) blockStep * axis2Step + 0.5D);
    currentSpot[axis3] = MathHelper.floor_double((double) from[axis3] + (double) blockStep * axis3Step + 0.5D);
    ...

This should prevent the replacement of solid blocks, but does not prevent branches coming through the corners. But that is the next part...

(Also, the placer algorithm has unnecessary floor and +0.5 for the main axis; the base values are always integers so adding 0.5 and the taking the floor of it leads to the same base values.)

Reason 2
The "line tracing" method used is one that will only visit one block along each block size step in a direction where the slope of the path is smallest. Thus, it will "slip" through block corners.

Here is the code for the current algorithm (including the fix 1 from above):

WorldGenBigTree.checkBlockLine()

...
        int endMainCoord = endDelta[largestEndDeltaAxis] + mainStep;
        int mainCoord = 0;
        for (mainCoord = 0; mainCoord != endMainCoord; mainCoord += mainStep) {
            currentSpot[largestEndDeltaAxis] = from[largestEndDeltaAxis] + mainCoord;
            currentSpot[axis2] = MathHelper.floor_double((double) from[axis2] + (double) mainCoord * axis2Step + 0.5D);
            currentSpot[axis3] = MathHelper.floor_double((double) from[axis3] + (double) mainCoord * axis3Step + 0.5D);
            int blockId = this.worldObj.getBlockId(currentSpot[0], currentSpot[1], currentSpot[2]);
            if (blockId != 0 && blockId != Block.leaves.blockID)
                break;
        }
        ...

Fix 2 - partial
Replace the foor loop and add the method:

...
        do {
            // Point of entering the block:
            currentSpot[largestEndDeltaAxis] = from[largestEndDeltaAxis] + mainCoord;
            //              start point          + travel so far         - half a step
            double minor2 = (from[axis2] + 0.5D) + mainCoord * axis2Step - 0.5 * axis2Step;
            double minor3 = (from[axis3] + 0.5D) + mainCoord * axis3Step - 0.5 * axis3Step;
            currentSpot[axis2] = MathHelper.floor_double(minor2);
            currentSpot[axis3] = MathHelper.floor_double(minor3);
            
            // At the end?
            if (currentSpot[0] == to[0] && currentSpot[1] == to[1] && currentSpot[2] == to[2])
                break;
            
            if (!blockIsOk(currentSpot[0], currentSpot[1], currentSpot[2]))
                break;
            
            // Check minor axis at the point of leaving the block:
            //       start point          + travel so far         + half a step
            //double newminor2 = (from[axis2] + 0.5D) + mainCoord * axis2Step + 0.5 * axis2Step;
            //double newminor3 = (from[axis3] + 0.5D) + mainCoord * axis3Step + 0.5 * axis3Step;
            double newMinor2 = minor2 + mainStep * axis2Step;
            double newMinor3 = minor3 + mainStep * axis3Step;
            
            boolean change2 = MathHelper.floor_double(newMinor2) != currentSpot[axis2];
            boolean change3 = MathHelper.floor_double(newMinor3) != currentSpot[axis3];
            if (change2 && change3) { // Need to step along both minor axis
                // TODO: resolve in which order they should be stepped.
                currentSpot[axis2] = MathHelper.floor_double(newMinor2);
                if (!blockIsOk(currentSpot[0], currentSpot[1], currentSpot[2]))
                    break;
                
                currentSpot[axis3] = MathHelper.floor_double(newMinor3);
                if (!blockIsOk(currentSpot[0], currentSpot[1], currentSpot[2]))
                    break;
            } else if (change2) { // Need to step along axis 2
                currentSpot[axis2] = MathHelper.floor_double(newMinor2);
                if (!blockIsOk(currentSpot[0], currentSpot[1], currentSpot[2]))
                    break;
            } else if (change3) { // Need to step along axis 3
                currentSpot[axis3] = MathHelper.floor_double(newMinor3);
                if (!blockIsOk(currentSpot[0], currentSpot[1], currentSpot[2]))
                    break;
            }
            
            mainCoord += 1;
        } while (true);
        ...

    private boolean blockIsOk(int x, int y, int z) {
        int blockId = this.worldObj.getBlockId(x, y, z);
        if (blockId != 0 && blockId != Block.leaves.blockID)
            return false;
        return true;
    }

The loop is not fully optimized, but that is actually a minor thing compared to the fact that both the original and fixed version use double-values with absolute coordinates. This is bad coding, able to cause some issue at extreme coordinates. The algorithm should work on relative values (i.e. tree root would be always at values 0,0,0), which the block read and write routines would use by first taking 'floor' and then add the offset coordinates as integers.

If interested in how it works, I found a page that explains it quite well, although the above version is organized ever so slightly differently, and is for 3D instead of just 2D. http://sinepost.wordpress.com/2012/05/24/drawing-in-a-straight-line/

There is also that minor TODO-point left, but that only causes almost completely imperceptible difference with the "correct" operation.

NOTE: the same fix should NOT be applied to the placeBlockLine() method, otherwise branches will look, uh, twisted?

Remaining 'issues'
Note, this will only prevent branches sticking through the shown open-corner cylinder shapes. Just like some screenshots show with closed-corner cylinders, with this fix it is still possible to have those leaves-only growths outside the cylinder. This is yet another bug in the check routine; it only checks that the trunk has space to grow, not that there is a way for leaves to grow. Alternately, the generator could be a bit smarter when placing the leaves, but then it still allows rather silly trees (just the trunk and leaves on top of it).

Affects 13w09b.

16 more comments

present in pre 1...

present in pre 2...

present in pre 3...
It was fixed in 1.8.2 PRE 4

@unknown, see at the top of this ticket, there is clearly written:

Status: Resolved
Resolution: Fixed
Fix Version/s: Minecraft 1.8.2-pre4

Sorry. It just didn't have the intermediate versions of the affects versions page and the comment confirming it had been fixed was:
Fixed in 1.8.2-pre4, possibly earlier.
Notice the 'possibly earlier'.

Michael Schiffner

(Unassigned)

Confirmed

Snapshot 13w07a, Snapshot 13w09a, Snapshot 13w09b, Snapshot 13w11a, Minecraft 1.6.4, ..., Minecraft 14w34d, Minecraft 1.8, Minecraft 1.8.1-pre1, Minecraft 1.8.1-pre3, Minecraft 1.8.1

Minecraft 1.8.2-pre4

Retrieved