Correctly teleport to highest block instead of spawn (fixes #178)

The fix for issue #57 added some limits to the values searched when looking for
a safe Y coordinate for the teleport destination, including calling
getHighestBlockYAt() to determine the Y coordinate of the highest non-empty
block.  In non-nether cases with normal terrain that doesn't approach the
world's maximum Y values, highestBlockBoundary will be set to 1 greater than the
return value of getHighestBlockYAt(), i.e. the Y coordinate of the empty block
above the highest non-empty block.

In the non-nether, non-flying case, limTop was previously set to this value, and
the safe Y coordinate search would proceed up to *but not including* this limit,
so the maximum Y coordinate searched to check if it was empty would be the
highest *non*-empty y coordinate.  However, the player's current Y coordinate is
checked first even if it is equal to limTop (unless it is somehow not greater
than limBot).  As a result, if the only safe block for the player to be
teleported on top of was getHighestBlockYAt(), they would only be teleported
there if it wouldn't result in a change in their Y coordinate, otherwise they
would be teleported to spawn.

This fix expands the search to include highestBlockBoundary.

highestBlockBoundary cannot be greater than world.getMaxHeight() - 2, so the
search for a Y coordinate will now include that value rather than excluding it.
isSafeSpot() only adds 1 to that value, giving world.getMaxHeight() - 1, which
is still within the valid Y coordinate range.

This might also fix issue #115.
This commit is contained in:
David O'Shea 2020-04-16 15:55:04 +09:30
parent f88ede397c
commit a1488146e1
1 changed files with 3 additions and 2 deletions

View File

@ -426,6 +426,7 @@ public class BorderData
// artificial height limit of 127 added for Nether worlds since CraftBukkit still incorrectly returns 255 for their max height, leading to players sent to the "roof" of the Nether
final boolean isNether = world.getEnvironment() == World.Environment.NETHER;
int limTop = isNether ? 125 : world.getMaxHeight() - 2;
// add 1 because getHighestBlockYAt() will give us the Y coordinate of a solid block, and we want the air block above it
final int highestBlockBoundary = Math.min(world.getHighestBlockYAt(X, Z) + 1, limTop);
// if Y is larger than the world can be and user can fly, return Y - Unless we are in the Nether, we might not want players on the roof
@ -448,9 +449,9 @@ public class BorderData
if (Y < limBot)
Y = limBot;
// for non Nether worlds we don't need to check upwards to the world-limit, it is enough to check up to the highestBlockBoundary, unless player is flying
// for non Nether worlds we don't need to check upwards to the world-limit, it is enough to check up to and including (hence the addition of 1) the highestBlockBoundary, unless player is flying
if (!isNether && !flying)
limTop = highestBlockBoundary;
limTop = highestBlockBoundary + 1;
// Expanding Y search method adapted from Acru's code in the Nether plugin
for(int y1 = Y, y2 = Y; (y1 > limBot) || (y2 < limTop); y1--, y2++){