From a1488146e125b528ff540b131e7b41bbd4a790a5 Mon Sep 17 00:00:00 2001 From: David O'Shea <2437134+dcoshea@users.noreply.github.com> Date: Thu, 16 Apr 2020 15:55:04 +0930 Subject: [PATCH 1/2] 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. --- src/main/java/com/wimbli/WorldBorder/BorderData.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/wimbli/WorldBorder/BorderData.java b/src/main/java/com/wimbli/WorldBorder/BorderData.java index 8217363..0f66b6a 100644 --- a/src/main/java/com/wimbli/WorldBorder/BorderData.java +++ b/src/main/java/com/wimbli/WorldBorder/BorderData.java @@ -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++){ From 74513b081199ae2a1d853315f49198f8eed141b0 Mon Sep 17 00:00:00 2001 From: David O'Shea <2437134+dcoshea@users.noreply.github.com> Date: Thu, 16 Apr 2020 20:32:34 +0930 Subject: [PATCH 2/2] 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. --- .../com/wimbli/WorldBorder/BorderData.java | 22 ++++++++++++------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/src/main/java/com/wimbli/WorldBorder/BorderData.java b/src/main/java/com/wimbli/WorldBorder/BorderData.java index 0f66b6a..1b015d1 100644 --- a/src/main/java/com/wimbli/WorldBorder/BorderData.java +++ b/src/main/java/com/wimbli/WorldBorder/BorderData.java @@ -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;