Allow teleporting player partially or fully above the world

For worlds other than the nether (which is a special case due to the
bedrock ceiling) the maximum Y coordinate to which a player would be
teleported was previously world.getMaxHeight() - 2.  This enabled
isSafeSpot() to check the blocks at Y and Y + 1 to ensure that they
were breathable.  However, this meant that if there were blocks all
the way up to 1 or 2 less than world.getMaxHeight(), the player would
never be teleported on top of them, and instead would be teleported to
an air gap somewhere below them, or to spawn if no such gap was found.

This commit enables the player to be teleported to Y coordinates up to
world.getMaxHeight() so that they can be teleported on top of the
highest possible blocks (which would be at world.getMaxHeight() - 1).
As part of this, isSafeSpot() is updated to treat the void at Y >=
world.getMaxHeight() as breathable.
This commit is contained in:
David O'Shea 2020-04-16 20:32:34 +09:30
parent a1488146e1
commit 74513b0811
1 changed files with 14 additions and 8 deletions

View File

@ -406,8 +406,13 @@ public class BorderData
// check if a particular spot consists of 2 breathable blocks over something relatively solid
private boolean isSafeSpot(World world, int X, int Y, int Z, boolean flying)
{
boolean safe = safeOpenBlocks.contains(world.getBlockAt(X, Y, Z).getType()) // target block open and safe
&& safeOpenBlocks.contains(world.getBlockAt(X, Y + 1, Z).getType()); // above target block open and safe
boolean safe =
// target block open and safe or is above maximum Y coordinate
(Y == world.getMaxHeight()
|| (safeOpenBlocks.contains(world.getBlockAt(X, Y, Z).getType())
// above target block open and safe or is above maximum Y coordinate
&& (Y + 1 == world.getMaxHeight()
|| safeOpenBlocks.contains(world.getBlockAt(X, Y + 1, Z).getType()))));
if (!safe || flying)
return safe;
@ -423,9 +428,9 @@ public class BorderData
// find closest safe Y position from the starting position
private double getSafeY(World world, int X, int Y, int Z, boolean flying)
{
// 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
// 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; we don't bother checking if Y = 126 or 127 are safe because they never will be unless there's a hole in the bedrock
final boolean isNether = world.getEnvironment() == World.Environment.NETHER;
int limTop = isNether ? 125 : world.getMaxHeight() - 2;
int limTop = isNether ? 125 : world.getMaxHeight();
// 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);
@ -449,12 +454,13 @@ 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 and including (hence the addition of 1) 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 the highestBlockBoundary, unless player is flying
if (!isNether && !flying)
limTop = highestBlockBoundary + 1;
limTop = highestBlockBoundary;
// 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++){
// Note that we want to include limTop in the search - in the extreme case, world.getMaxHeight() should be included since the player can stand on top of the highest block
for(int y1 = Y, y2 = Y; (y1 > limBot) || (y2 <= limTop); y1--, y2++){
// Look below.
if(y1 > limBot)
{
@ -463,7 +469,7 @@ public class BorderData
}
// Look above.
if(y2 < limTop && y2 != y1)
if(y2 <= limTop && y2 != y1)
{
if (isSafeSpot(world, X, y2, Z, flying))
return (double)y2;