Note: The description is based on a decompiled version of Minecraft 1.15.2 using yarn. This issue is barely noticeable without enabling debug logging or looking at the source code.
The net.minecraft.block.entity.EndGatewayBlockEntity.findPortalPosition(WorldChunk worldChunk)
method always returns the position of the block in the southeast corner of the worldChunk
, regardless of whether it is a valid block or not.
I've also tested this in Minecraft 1.9.4, 1.12, 1.13.2 and 1.14. It seems that this has been an issue since 1.14.
How to reproduce
1. Enable Log4j debug logging and launch the game
2. Create a default world with seed 1327593420388419957
3. Enter the end dimension
4. Kill the ender dragon, then enter the entrance gateway
5. Open latest.log in (Minecraft directory)\logs and search for "Found block at"
6. Note that the coordinates are {x=-833, y=63, z=-609} and the block there is minecraft:air
Code analysis
This issue is likely caused by the net.minecraft.util.BlockPos.iterate(int minX, int maxX, ...)
method returning a BlockPos
from an instance of BlockPos.Mutable
(unlike in 1.12, where the method returns a new BlockPos
instead).
Possible fix
Change one line of code in findPortalPosition(WorldChunk worldChunk)
blockPos3 = blockPos4;
to
blockPos3 = blockPos4.toImmutable();
While using "for(Iterator i: Iterable<BlockPos>)", it is the same as "for(Iterator i = Iterable.Iterator();Iterable.hasNext();Iterable.next())".
There is a mistake in "betweenClosed(final int n, final int n2, final int n3, int n4, int n5, int n6)" ---- change values of the same reference.
Each time the code arrives in "computeNext()" in the Iterable, this.curser.set() changes the x,y,z values of the same MutableBlockPos(reference type, extends BlockPos) and then makes "blockpos4" refers to it. When an eligible endstone was found, the blockpos3 refers to blockpos4, which is in fact the reference of the only object of MutableBlockPos.
From now on, each time computeNext() changes the coordinates of the only object of MutableBlockPos, the blockpos3 changes with it, because all BlockPos (including blockpos3), pointing to the same object, or it can be said the same memory area in the heap.
So according to the indexing order, the final result is: If there is an eligible endstone, the method will return the max x,y,z (actually n4, n5 and n6 in method betweenClosed) in the indexing area. This is the southeast and highest block in the area. If there is no eligible endstone, return null.
There is another way to solve this:
Change blockpos3 to 3 double values representing x, y and z. Change "blockpos3 = blockpos 4" to "assign x, y, z with blockpos4" and finally "return new BlockPos(x, y, z)". This way reduces creating more BlockPos objects, just one. And three Integer which hardly affect performance.