From 64e7baec3c38c5fc6f24b388bd6ef144f0dd5bf3 Mon Sep 17 00:00:00 2001 From: Ben Woo <30431861+benwoo1110@users.noreply.github.com> Date: Tue, 14 Jan 2025 23:10:35 +0800 Subject: [PATCH 1/6] Implement initial new algorithm for safe location --- .../teleportation/AdvancedBlockSafety.java | 132 ++++++++++++++++++ .../teleportation/AsyncSafetyTeleporter.java | 4 +- .../AsyncSafetyTeleporterAction.java | 6 +- 3 files changed, 137 insertions(+), 5 deletions(-) create mode 100644 src/main/java/org/mvplugins/multiverse/core/teleportation/AdvancedBlockSafety.java diff --git a/src/main/java/org/mvplugins/multiverse/core/teleportation/AdvancedBlockSafety.java b/src/main/java/org/mvplugins/multiverse/core/teleportation/AdvancedBlockSafety.java new file mode 100644 index 00000000..efe57dba --- /dev/null +++ b/src/main/java/org/mvplugins/multiverse/core/teleportation/AdvancedBlockSafety.java @@ -0,0 +1,132 @@ +package org.mvplugins.multiverse.core.teleportation; + +import com.dumptruckman.minecraft.util.Logging; +import jakarta.inject.Inject; +import org.bukkit.Location; +import org.bukkit.Material; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.jvnet.hk2.annotations.Service; + +@Service +public class AdvancedBlockSafety { + + @Inject + private AdvancedBlockSafety() { + } + + private boolean playerCanSpawnHereSafely(@NotNull Location location) { + if (isUnsafeSpawnBody(location)) { + // Player body will be stuck in solid + Logging.finest("Unsafe location for player's body."); + return false; + } + Location airBlockForHead = offsetLocation(location, 0, 1, 0); + if (isUnsafeSpawnBody(airBlockForHead)) { + // Player's head will be stuck in solid + Logging.finest("Unsafe location for player's head."); + return false; + } + Location standingOnBlock = offsetLocation(location, 0, -1, 0); + if (isUnsafeSpawnPlatform(standingOnBlock)) { + // Player will drop down + Logging.finest("Unsafe location due to invalid platform."); + return false; + } + return true; + } + + /** + * Player's body must be in non-solid block that is non-harming. + * + * @param location + * @return + */ + private boolean isUnsafeSpawnBody(@NotNull Location location) { + Material blockMaterial = location.getBlock().getType(); + return blockMaterial.isSolid() || blockMaterial == Material.FIRE; + } + + /** + * Player must stand on solid ground, or water that is only 1 block deep to prevent drowning. + * + * @param location + * @return + */ + private boolean isUnsafeSpawnPlatform(@NotNull Location location) { + return !location.getBlock().getType().isSolid() || isDeepWater(location); + } + + /** + * Water that is 2 or more block deep + * + * @param location + * @return + */ + private boolean isDeepWater(@NotNull Location location) { + if (location.getBlock().getType() != Material.WATER) { + return false; + } + return offsetLocation(location, 0, -1, 0).getBlock().getType() == Material.WATER; + } + + @Nullable + public Location adjustSafeSpawnLocation(@NotNull Location location) { + return adjustSafeSpawnLocation(location, 3, 2); + } + + @Nullable + public Location adjustSafeSpawnLocation(@NotNull Location location, int horizontal, int vertical) { + int[] horizontalSpan = rangeSpan(horizontal); + int[] verticalSpan = rangeSpan(vertical); + + for(int y : verticalSpan) { + for (int x : horizontalSpan) { + for (int z : horizontalSpan) { + Logging.finest("Checking offset: %s, %s, %s", x, y, z); + Location offsetLocation = offsetLocation(location, x, y, z); + if (playerCanSpawnHereSafely(offsetLocation)) { + // Set location to the center of the block + offsetLocation.setX(offsetLocation.getBlockX() + 0.5); + offsetLocation.setZ(offsetLocation.getBlockZ() + 0.5); + return offsetLocation; + } + } + } + } + return null; + } + + /** + * Plus minus range from 0, starting with the closer offset. + * E.g. 0, 1, -1, 2, -2... + * + * @param number + * @return + */ + private int[] rangeSpan(int number) { + int[] numArray = new int[number * 2 + 1]; + numArray[0] = 0; + for (int i = 1; i <= number; i++) { + numArray[i * 2 - 1] = i; + numArray[i * 2] = -i; + } + return numArray; + } + + /** + * Clones and creates a new location with the given offset. + * + * @param location + * @param x + * @param y + * @param z + * @return + */ + @NotNull + private Location offsetLocation(@NotNull Location location, double x, double y, double z) { + Location newLocation = location.clone(); + newLocation.add(x, y, z); + return newLocation; + } +} diff --git a/src/main/java/org/mvplugins/multiverse/core/teleportation/AsyncSafetyTeleporter.java b/src/main/java/org/mvplugins/multiverse/core/teleportation/AsyncSafetyTeleporter.java index ae5e0ea6..24f3fb05 100644 --- a/src/main/java/org/mvplugins/multiverse/core/teleportation/AsyncSafetyTeleporter.java +++ b/src/main/java/org/mvplugins/multiverse/core/teleportation/AsyncSafetyTeleporter.java @@ -16,13 +16,13 @@ import org.mvplugins.multiverse.core.destination.DestinationInstance; */ @Service public class AsyncSafetyTeleporter { - private final BlockSafety blockSafety; + private final AdvancedBlockSafety blockSafety; private final TeleportQueue teleportQueue; private final PluginManager pluginManager; @Inject AsyncSafetyTeleporter( - @NotNull BlockSafety blockSafety, + @NotNull AdvancedBlockSafety blockSafety, @NotNull TeleportQueue teleportQueue, @NotNull PluginManager pluginManager) { this.blockSafety = blockSafety; diff --git a/src/main/java/org/mvplugins/multiverse/core/teleportation/AsyncSafetyTeleporterAction.java b/src/main/java/org/mvplugins/multiverse/core/teleportation/AsyncSafetyTeleporterAction.java index 9bbbc844..3803a3bd 100644 --- a/src/main/java/org/mvplugins/multiverse/core/teleportation/AsyncSafetyTeleporterAction.java +++ b/src/main/java/org/mvplugins/multiverse/core/teleportation/AsyncSafetyTeleporterAction.java @@ -25,7 +25,7 @@ import java.util.List; */ public class AsyncSafetyTeleporterAction { - private final BlockSafety blockSafety; + private final AdvancedBlockSafety blockSafety; private final TeleportQueue teleportQueue; private final PluginManager pluginManager; @@ -34,7 +34,7 @@ public class AsyncSafetyTeleporterAction { private @Nullable CommandSender teleporter = null; AsyncSafetyTeleporterAction( - @NotNull BlockSafety blockSafety, + @NotNull AdvancedBlockSafety blockSafety, @NotNull TeleportQueue teleportQueue, @NotNull PluginManager pluginManager, @NotNull Either> locationOrDestination) { @@ -146,7 +146,7 @@ public class AsyncSafetyTeleporterAction { if (!this.checkSafety) { return Attempt.success(location); } - Location safeLocation = blockSafety.getSafeLocation(location); + Location safeLocation = blockSafety.adjustSafeSpawnLocation(location); if (safeLocation == null) { return Attempt.failure(TeleportFailureReason.UNSAFE_LOCATION); } From 3b9a91c01030993ba76b2d631efe03a7db72ac4b Mon Sep 17 00:00:00 2001 From: Ben Woo <30431861+benwoo1110@users.noreply.github.com> Date: Wed, 15 Jan 2025 12:43:44 +0800 Subject: [PATCH 2/6] Implement better search algo that search from the nearest block outwards --- .../teleportation/AdvancedBlockSafety.java | 158 +++++++++++------- .../AsyncSafetyTeleporterAction.java | 1 - 2 files changed, 100 insertions(+), 59 deletions(-) diff --git a/src/main/java/org/mvplugins/multiverse/core/teleportation/AdvancedBlockSafety.java b/src/main/java/org/mvplugins/multiverse/core/teleportation/AdvancedBlockSafety.java index efe57dba..bd328837 100644 --- a/src/main/java/org/mvplugins/multiverse/core/teleportation/AdvancedBlockSafety.java +++ b/src/main/java/org/mvplugins/multiverse/core/teleportation/AdvancedBlockSafety.java @@ -4,6 +4,7 @@ import com.dumptruckman.minecraft.util.Logging; import jakarta.inject.Inject; import org.bukkit.Location; import org.bukkit.Material; +import org.bukkit.block.Block; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jvnet.hk2.annotations.Service; @@ -11,23 +12,31 @@ import org.jvnet.hk2.annotations.Service; @Service public class AdvancedBlockSafety { + // This will search a maximum of 7 * 6 * 7 = 294 blocks + public static final int DEFAULT_HORIZONTAL_RANGE = 3; + public static final int DEFAULT_VERTICAL_RANGE = 2; + @Inject private AdvancedBlockSafety() { } - private boolean playerCanSpawnHereSafely(@NotNull Location location) { - if (isUnsafeSpawnBody(location)) { + public boolean playerCanSpawnSafelyAt(@NotNull Location location) { + return playerCanSpawnSafelyAt(location.getBlock()); + } + + public boolean playerCanSpawnSafelyAt(@NotNull Block block) { + if (isUnsafeSpawnBody(block)) { // Player body will be stuck in solid Logging.finest("Unsafe location for player's body."); return false; } - Location airBlockForHead = offsetLocation(location, 0, 1, 0); + Block airBlockForHead = block.getRelative(0, 1, 0); if (isUnsafeSpawnBody(airBlockForHead)) { // Player's head will be stuck in solid Logging.finest("Unsafe location for player's head."); return false; } - Location standingOnBlock = offsetLocation(location, 0, -1, 0); + Block standingOnBlock = block.getRelative(0, -1, 0); if (isUnsafeSpawnPlatform(standingOnBlock)) { // Player will drop down Logging.finest("Unsafe location due to invalid platform."); @@ -39,94 +48,127 @@ public class AdvancedBlockSafety { /** * Player's body must be in non-solid block that is non-harming. * - * @param location + * @param block * @return */ - private boolean isUnsafeSpawnBody(@NotNull Location location) { - Material blockMaterial = location.getBlock().getType(); + private boolean isUnsafeSpawnBody(@NotNull Block block) { + Material blockMaterial = block.getType(); return blockMaterial.isSolid() || blockMaterial == Material.FIRE; } /** * Player must stand on solid ground, or water that is only 1 block deep to prevent drowning. * - * @param location + * @param block * @return */ - private boolean isUnsafeSpawnPlatform(@NotNull Location location) { - return !location.getBlock().getType().isSolid() || isDeepWater(location); + private boolean isUnsafeSpawnPlatform(@NotNull Block block) { + return !block.getType().isSolid() || isDeepWater(block); } /** * Water that is 2 or more block deep * - * @param location + * @param block * @return */ - private boolean isDeepWater(@NotNull Location location) { - if (location.getBlock().getType() != Material.WATER) { + private boolean isDeepWater(@NotNull Block block) { + if (block.getType() != Material.WATER) { return false; } - return offsetLocation(location, 0, -1, 0).getBlock().getType() == Material.WATER; + return block.getRelative(0, -1, 0).getType() == Material.WATER; } @Nullable public Location adjustSafeSpawnLocation(@NotNull Location location) { - return adjustSafeSpawnLocation(location, 3, 2); + return adjustSafeSpawnLocation(location, DEFAULT_HORIZONTAL_RANGE, DEFAULT_VERTICAL_RANGE); } @Nullable - public Location adjustSafeSpawnLocation(@NotNull Location location, int horizontal, int vertical) { - int[] horizontalSpan = rangeSpan(horizontal); - int[] verticalSpan = rangeSpan(vertical); + public Location adjustSafeSpawnLocation(@NotNull Location location, int horizontalRange, int verticalRange) { + Block safeBlock = adjustSafeSpawnBlock(location.getBlock(), horizontalRange, verticalRange); + if (safeBlock == null) { + return null; + } + Location safeLocation = safeBlock.getLocation(); + // Adjust to center of block + safeLocation.add(0.5, 0, 0.5); + return safeLocation; + } - for(int y : verticalSpan) { - for (int x : horizontalSpan) { - for (int z : horizontalSpan) { - Logging.finest("Checking offset: %s, %s, %s", x, y, z); - Location offsetLocation = offsetLocation(location, x, y, z); - if (playerCanSpawnHereSafely(offsetLocation)) { - // Set location to the center of the block - offsetLocation.setX(offsetLocation.getBlockX() + 0.5); - offsetLocation.setZ(offsetLocation.getBlockZ() + 0.5); - return offsetLocation; - } - } + @Nullable + public Block adjustSafeSpawnBlock(@NotNull Block block) { + return adjustSafeSpawnBlock(block, DEFAULT_HORIZONTAL_RANGE, DEFAULT_VERTICAL_RANGE); + } + + @Nullable + public Block adjustSafeSpawnBlock(@NotNull Block block, int horizontalRange, int verticalRange) { + Block searchResult = searchAroundXZ(block, horizontalRange); + if (searchResult != null) { + return searchResult; + } + for (int i = 1; i <= verticalRange; i++) { + searchResult = searchAroundXZ(block.getRelative(0, i, 0), horizontalRange); + if (searchResult != null) { + return searchResult; + } + searchResult = searchAroundXZ(block.getRelative(0, -i, 0), horizontalRange); + if (searchResult != null) { + return searchResult; } } return null; } - /** - * Plus minus range from 0, starting with the closer offset. - * E.g. 0, 1, -1, 2, -2... - * - * @param number - * @return - */ - private int[] rangeSpan(int number) { - int[] numArray = new int[number * 2 + 1]; - numArray[0] = 0; - for (int i = 1; i <= number; i++) { - numArray[i * 2 - 1] = i; - numArray[i * 2] = -i; + @Nullable + private Block searchAroundXZ(Block block, int radius) { + if (playerCanSpawnSafelyAt(block)) { + return block; } - return numArray; + for (int r = 1; r <= radius; r++) { + boolean radiusX = true; + boolean incrementOffset = false; + int offset = 0; + int noOfIterations = r * 2 + 1; + for (int i = 0; i < noOfIterations; i++) { + Block searchResult = radiusX + ? searchPlusMinusPermutation(block, r, offset) + : searchPlusMinusPermutation(block, offset, r); + if (searchResult != null) { + return searchResult; + } + if (incrementOffset) { + offset++; + } + radiusX = !radiusX; + incrementOffset = !incrementOffset; + } + } + return null; } - /** - * Clones and creates a new location with the given offset. - * - * @param location - * @param x - * @param y - * @param z - * @return - */ - @NotNull - private Location offsetLocation(@NotNull Location location, double x, double y, double z) { - Location newLocation = location.clone(); - newLocation.add(x, y, z); - return newLocation; + @Nullable + private Block searchPlusMinusPermutation(Block block, int x, int z) { + Block relative = block.getRelative(-x, 0, z); + if (playerCanSpawnSafelyAt(relative)) { + return relative; + } + if (x != 0) { + relative = block.getRelative(-x, 0, -z); + if (playerCanSpawnSafelyAt(relative)) { + return relative; + } + } + relative = block.getRelative(x, 0, z); + if (playerCanSpawnSafelyAt(relative)) { + return relative; + } + if (z != 0) { + relative = block.getRelative(x, 0, -z); + if (playerCanSpawnSafelyAt(relative)) { + return relative; + } + } + return null; } } diff --git a/src/main/java/org/mvplugins/multiverse/core/teleportation/AsyncSafetyTeleporterAction.java b/src/main/java/org/mvplugins/multiverse/core/teleportation/AsyncSafetyTeleporterAction.java index 3803a3bd..da7aca73 100644 --- a/src/main/java/org/mvplugins/multiverse/core/teleportation/AsyncSafetyTeleporterAction.java +++ b/src/main/java/org/mvplugins/multiverse/core/teleportation/AsyncSafetyTeleporterAction.java @@ -11,7 +11,6 @@ import org.bukkit.entity.Player; import org.bukkit.plugin.PluginManager; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import org.mvplugins.multiverse.core.api.BlockSafety; import org.mvplugins.multiverse.core.destination.DestinationInstance; import org.mvplugins.multiverse.core.event.MVTeleportDestinationEvent; import org.mvplugins.multiverse.core.utils.result.Async; From 2df1f39ef96a35e1805315673cc8ffa62ff71ece Mon Sep 17 00:00:00 2001 From: Ben Woo <30431861+benwoo1110@users.noreply.github.com> Date: Wed, 15 Jan 2025 14:11:01 +0800 Subject: [PATCH 3/6] Implement rest of block safety methods --- .../teleportation/AdvancedBlockSafety.java | 143 +++++++++++++++--- 1 file changed, 120 insertions(+), 23 deletions(-) diff --git a/src/main/java/org/mvplugins/multiverse/core/teleportation/AdvancedBlockSafety.java b/src/main/java/org/mvplugins/multiverse/core/teleportation/AdvancedBlockSafety.java index bd328837..43cf6cc1 100644 --- a/src/main/java/org/mvplugins/multiverse/core/teleportation/AdvancedBlockSafety.java +++ b/src/main/java/org/mvplugins/multiverse/core/teleportation/AdvancedBlockSafety.java @@ -5,10 +5,17 @@ import jakarta.inject.Inject; import org.bukkit.Location; import org.bukkit.Material; import org.bukkit.block.Block; +import org.bukkit.block.data.Rail; +import org.bukkit.entity.Minecart; +import org.bukkit.entity.Vehicle; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jvnet.hk2.annotations.Service; +import org.mvplugins.multiverse.core.api.LocationManipulation; +/** + * + */ @Service public class AdvancedBlockSafety { @@ -16,8 +23,11 @@ public class AdvancedBlockSafety { public static final int DEFAULT_HORIZONTAL_RANGE = 3; public static final int DEFAULT_VERTICAL_RANGE = 2; + private final LocationManipulation locationManipulation; + @Inject - private AdvancedBlockSafety() { + AdvancedBlockSafety(@NotNull LocationManipulation locationManipulation) { + this.locationManipulation = locationManipulation; } public boolean playerCanSpawnSafelyAt(@NotNull Location location) { @@ -25,6 +35,7 @@ public class AdvancedBlockSafety { } public boolean playerCanSpawnSafelyAt(@NotNull Block block) { + Logging.finest("Checking spawn safety for location: %s, %s, %s", block.getX(), block.getY(), block.getZ()); if (isUnsafeSpawnBody(block)) { // Player body will be stuck in solid Logging.finest("Unsafe location for player's body."); @@ -42,6 +53,7 @@ public class AdvancedBlockSafety { Logging.finest("Unsafe location due to invalid platform."); return false; } + Logging.finest("Location is safe."); return true; } @@ -79,47 +91,88 @@ public class AdvancedBlockSafety { return block.getRelative(0, -1, 0).getType() == Material.WATER; } + /** + * + * + * @param location + * @return The safe location, or null + */ @Nullable public Location adjustSafeSpawnLocation(@NotNull Location location) { return adjustSafeSpawnLocation(location, DEFAULT_HORIZONTAL_RANGE, DEFAULT_VERTICAL_RANGE); } + /** + * + * @param location + * @param horizontalRange + * @param verticalRange + * @return The safe location, or null + */ @Nullable public Location adjustSafeSpawnLocation(@NotNull Location location, int horizontalRange, int verticalRange) { Block safeBlock = adjustSafeSpawnBlock(location.getBlock(), horizontalRange, verticalRange); if (safeBlock == null) { return null; } - Location safeLocation = safeBlock.getLocation(); - // Adjust to center of block - safeLocation.add(0.5, 0, 0.5); - return safeLocation; + return new Location( + location.getWorld(), + safeBlock.getX() + 0.5, + safeBlock.getY(), + safeBlock.getZ() + 0.5, + location.getYaw(), + location.getPitch()); } + /** + * + * @param block + * @return The safe block, or null + */ @Nullable public Block adjustSafeSpawnBlock(@NotNull Block block) { return adjustSafeSpawnBlock(block, DEFAULT_HORIZONTAL_RANGE, DEFAULT_VERTICAL_RANGE); } + /** + * + * @param block + * @param horizontalRange + * @param verticalRange + * @return The safe block, or null + */ @Nullable public Block adjustSafeSpawnBlock(@NotNull Block block, int horizontalRange, int verticalRange) { Block searchResult = searchAroundXZ(block, horizontalRange); if (searchResult != null) { return searchResult; } + int maxHeight = block.getWorld().getMaxHeight(); + int minHeight = block.getWorld().getMinHeight(); for (int i = 1; i <= verticalRange; i++) { - searchResult = searchAroundXZ(block.getRelative(0, i, 0), horizontalRange); - if (searchResult != null) { - return searchResult; + if (block.getY() + i < maxHeight) { + searchResult = searchAroundXZ(block.getRelative(0, i, 0), horizontalRange); + if (searchResult != null) { + return searchResult; + } } - searchResult = searchAroundXZ(block.getRelative(0, -i, 0), horizontalRange); - if (searchResult != null) { - return searchResult; + if (block.getY() - i >= minHeight) { + searchResult = searchAroundXZ(block.getRelative(0, -i, 0), horizontalRange); + if (searchResult != null) { + return searchResult; + } } } return null; } + /** + * Search a square from n - radius to n + radius for both x and z + * + * @param block + * @param radius + * @return The safe block, or null + */ @Nullable private Block searchAroundXZ(Block block, int radius) { if (playerCanSpawnSafelyAt(block)) { @@ -147,28 +200,72 @@ public class AdvancedBlockSafety { return null; } + /** + * Search 4 relative blocks with the following offsets: (-x, -z) (-x, z) (x, -z) (x, z) + * + * @param block The block to be relative to + * @param x Amount to offset for the x axis + * @param z Amount to offset for the z axis + * @return The safe block, or null + */ @Nullable private Block searchPlusMinusPermutation(Block block, int x, int z) { - Block relative = block.getRelative(-x, 0, z); - if (playerCanSpawnSafelyAt(relative)) { - return relative; - } - if (x != 0) { - relative = block.getRelative(-x, 0, -z); - if (playerCanSpawnSafelyAt(relative)) { - return relative; - } - } - relative = block.getRelative(x, 0, z); + Block relative = block.getRelative(-x, 0, -z); if (playerCanSpawnSafelyAt(relative)) { return relative; } if (z != 0) { - relative = block.getRelative(x, 0, -z); + relative = block.getRelative(-x, 0, z); if (playerCanSpawnSafelyAt(relative)) { return relative; } } + if (x != 0) { + relative = block.getRelative(x, 0, -z); + if (playerCanSpawnSafelyAt(relative)) { + return relative; + } + if (z != 0) { + relative = block.getRelative(x, 0, z); + if (playerCanSpawnSafelyAt(relative)) { + return relative; + } + } + } return null; } + + public boolean isBlockAboveAir(Location location) { + return location.getBlock().getRelative(0, -1, 0).getType().isAir(); + } + + /** + * + * @param location + * @return + */ + public boolean isEntityOnTrack(Location location) { + return location.getBlock().getBlockData() instanceof Rail; + } + + /** + * + * @param cart + * @return + */ + public boolean canSpawnCartSafely(Minecart cart) { + if (isBlockAboveAir(cart.getLocation())) { + return true; + } + return isEntityOnTrack(locationManipulation.getNextBlock(cart)); + } + + /** + * + * @param vehicle + * @return + */ + public boolean canSpawnVehicleSafely(Vehicle vehicle) { + return isBlockAboveAir(vehicle.getLocation()); + } } From cb2ab3e4378a0e709debc096f2fea07cd02309a2 Mon Sep 17 00:00:00 2001 From: Ben Woo <30431861+benwoo1110@users.noreply.github.com> Date: Thu, 16 Jan 2025 09:31:53 +0800 Subject: [PATCH 4/6] Shift all to use AdvancedBlockSafety and deprecate old BlockSafety --- .../multiverse/core/api/BlockSafety.java | 1 + .../multiverse/core/api/MVConfig.java | 8 + .../multiverse/core/config/MVCoreConfig.java | 20 ++ .../core/config/MVCoreConfigNodes.java | 14 + .../core/listeners/MVPlayerListener.java | 10 +- .../teleportation/AdvancedBlockSafety.java | 259 +++++++++++++----- .../teleportation/AsyncSafetyTeleporter.java | 1 - .../AsyncSafetyTeleporterAction.java | 2 +- .../core/teleportation/SimpleBlockSafety.java | 2 +- .../core/world/LoadedMultiverseWorld.java | 13 +- .../multiverse/core/world/WorldManager.java | 6 +- 11 files changed, 248 insertions(+), 88 deletions(-) diff --git a/src/main/java/org/mvplugins/multiverse/core/api/BlockSafety.java b/src/main/java/org/mvplugins/multiverse/core/api/BlockSafety.java index 162ef5f9..a3694ddc 100644 --- a/src/main/java/org/mvplugins/multiverse/core/api/BlockSafety.java +++ b/src/main/java/org/mvplugins/multiverse/core/api/BlockSafety.java @@ -10,6 +10,7 @@ import org.jvnet.hk2.annotations.Contract; /** * Used to get block/location-related information. */ +@Deprecated @Contract public interface BlockSafety { /** diff --git a/src/main/java/org/mvplugins/multiverse/core/api/MVConfig.java b/src/main/java/org/mvplugins/multiverse/core/api/MVConfig.java index e601d17a..0947c882 100644 --- a/src/main/java/org/mvplugins/multiverse/core/api/MVConfig.java +++ b/src/main/java/org/mvplugins/multiverse/core/api/MVConfig.java @@ -112,6 +112,14 @@ public interface MVConfig { */ void setFirstSpawnOverride(boolean firstSpawnOverride); + void setSafeLocationHorizontalSearchRadius(int searchRadius); + + int getSafeLocationHorizontalSearchRadius(); + + void setSafeLocationVerticalSearchRadius(int searchRadius); + + int getSafeLocationVerticalSearchRadius(); + /** * Gets firstSpawnOverride. * @return firstSpawnOverride. diff --git a/src/main/java/org/mvplugins/multiverse/core/config/MVCoreConfig.java b/src/main/java/org/mvplugins/multiverse/core/config/MVCoreConfig.java index a4746fa0..8a1d26d5 100644 --- a/src/main/java/org/mvplugins/multiverse/core/config/MVCoreConfig.java +++ b/src/main/java/org/mvplugins/multiverse/core/config/MVCoreConfig.java @@ -189,6 +189,26 @@ public class MVCoreConfig implements MVConfig { configHandle.set(configNodes.FIRST_SPAWN_OVERRIDE, firstSpawnOverride); } + @Override + public void setSafeLocationHorizontalSearchRadius(int searchRadius) { + configHandle.set(configNodes.SAFE_LOCATION_HORIZONTAL_SEARCH_RADIUS, searchRadius); + } + + @Override + public int getSafeLocationHorizontalSearchRadius() { + return configHandle.get(configNodes.SAFE_LOCATION_HORIZONTAL_SEARCH_RADIUS); + } + + @Override + public void setSafeLocationVerticalSearchRadius(int searchRadius) { + configHandle.set(configNodes.SAFE_LOCATION_VERTICAL_SEARCH_RADIUS, searchRadius); + } + + @Override + public int getSafeLocationVerticalSearchRadius() { + return configHandle.get(configNodes.SAFE_LOCATION_VERTICAL_SEARCH_RADIUS); + } + @Override public boolean getFirstSpawnOverride() { return configHandle.get(configNodes.FIRST_SPAWN_OVERRIDE); diff --git a/src/main/java/org/mvplugins/multiverse/core/config/MVCoreConfigNodes.java b/src/main/java/org/mvplugins/multiverse/core/config/MVCoreConfigNodes.java index 5b8b9195..f61c075a 100644 --- a/src/main/java/org/mvplugins/multiverse/core/config/MVCoreConfigNodes.java +++ b/src/main/java/org/mvplugins/multiverse/core/config/MVCoreConfigNodes.java @@ -130,6 +130,20 @@ class MVCoreConfigNodes { .name("teleport-intercept") .build()); + final ConfigNode SAFE_LOCATION_HORIZONTAL_SEARCH_RADIUS = node(ConfigNode.builder("teleport.safe-location-horizontal-search-radius", Integer.class) + .comment("") + .comment("Sets the horizontal search radius for finding a safe location to teleport to.") + .defaultValue(3) + .name("safe-location-horizontal-search-radius") + .build()); + + final ConfigNode SAFE_LOCATION_VERTICAL_SEARCH_RADIUS = node(ConfigNode.builder("teleport.safe-location-vertical-search-radius", Integer.class) + .comment("") + .comment("Sets the vertical search radius for finding a safe location to teleport to.") + .defaultValue(3) + .name("safe-location-vertical-search-radius") + .build()); + private final ConfigHeaderNode SPAWN_HEADER = node(ConfigHeaderNode.builder("spawn") .comment("") .comment("") diff --git a/src/main/java/org/mvplugins/multiverse/core/listeners/MVPlayerListener.java b/src/main/java/org/mvplugins/multiverse/core/listeners/MVPlayerListener.java index 389f174b..abdd3942 100644 --- a/src/main/java/org/mvplugins/multiverse/core/listeners/MVPlayerListener.java +++ b/src/main/java/org/mvplugins/multiverse/core/listeners/MVPlayerListener.java @@ -8,7 +8,6 @@ package org.mvplugins.multiverse.core.listeners; import java.util.Map; -import java.util.Optional; import java.util.concurrent.ConcurrentHashMap; import com.dumptruckman.minecraft.util.Logging; @@ -24,22 +23,20 @@ import org.bukkit.entity.Player; import org.bukkit.event.EventHandler; import org.bukkit.event.EventPriority; import org.bukkit.event.player.PlayerChangedWorldEvent; -import org.bukkit.event.player.PlayerJoinEvent; import org.bukkit.event.player.PlayerPortalEvent; import org.bukkit.event.player.PlayerRespawnEvent; import org.bukkit.event.player.PlayerTeleportEvent; import org.bukkit.plugin.Plugin; -import org.jetbrains.annotations.NotNull; import org.jvnet.hk2.annotations.Service; import org.mvplugins.multiverse.core.MultiverseCore; -import org.mvplugins.multiverse.core.api.BlockSafety; import org.mvplugins.multiverse.core.commandtools.MVCommandManager; import org.mvplugins.multiverse.core.config.MVCoreConfig; import org.mvplugins.multiverse.core.destination.DestinationInstance; import org.mvplugins.multiverse.core.destination.DestinationsProvider; import org.mvplugins.multiverse.core.economy.MVEconomist; import org.mvplugins.multiverse.core.event.MVRespawnEvent; +import org.mvplugins.multiverse.core.teleportation.AdvancedBlockSafety; import org.mvplugins.multiverse.core.teleportation.AsyncSafetyTeleporter; import org.mvplugins.multiverse.core.teleportation.TeleportQueue; import org.mvplugins.multiverse.core.utils.result.ResultChain; @@ -58,7 +55,7 @@ public class MVPlayerListener implements CoreListener { private final Plugin plugin; private final MVCoreConfig config; private final Provider worldManagerProvider; - private final BlockSafety blockSafety; + private final AdvancedBlockSafety blockSafety; private final AsyncSafetyTeleporter safetyTeleporter; private final Server server; private final TeleportQueue teleportQueue; @@ -75,7 +72,7 @@ public class MVPlayerListener implements CoreListener { MultiverseCore plugin, MVCoreConfig config, Provider worldManagerProvider, - BlockSafety blockSafety, + AdvancedBlockSafety blockSafety, AsyncSafetyTeleporter safetyTeleporter, Server server, TeleportQueue teleportQueue, @@ -317,6 +314,7 @@ public class MVPlayerListener implements CoreListener { return; } } + /** * This method is called when a player actually portals via a vanilla style portal. * @param event The Event that was fired. diff --git a/src/main/java/org/mvplugins/multiverse/core/teleportation/AdvancedBlockSafety.java b/src/main/java/org/mvplugins/multiverse/core/teleportation/AdvancedBlockSafety.java index 43cf6cc1..4a91f9a1 100644 --- a/src/main/java/org/mvplugins/multiverse/core/teleportation/AdvancedBlockSafety.java +++ b/src/main/java/org/mvplugins/multiverse/core/teleportation/AdvancedBlockSafety.java @@ -1,10 +1,13 @@ package org.mvplugins.multiverse.core.teleportation; import com.dumptruckman.minecraft.util.Logging; +import io.vavr.control.Option; import jakarta.inject.Inject; import org.bukkit.Location; import org.bukkit.Material; +import org.bukkit.World; import org.bukkit.block.Block; +import org.bukkit.block.BlockFace; import org.bukkit.block.data.Rail; import org.bukkit.entity.Minecart; import org.bukkit.entity.Vehicle; @@ -12,29 +15,123 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jvnet.hk2.annotations.Service; import org.mvplugins.multiverse.core.api.LocationManipulation; +import org.mvplugins.multiverse.core.config.MVCoreConfig; /** - * + * Used to check get or find block/location-related information. */ @Service public class AdvancedBlockSafety { - // This will search a maximum of 7 * 6 * 7 = 294 blocks - public static final int DEFAULT_HORIZONTAL_RANGE = 3; - public static final int DEFAULT_VERTICAL_RANGE = 2; - + private final MVCoreConfig config; private final LocationManipulation locationManipulation; @Inject - AdvancedBlockSafety(@NotNull LocationManipulation locationManipulation) { + AdvancedBlockSafety(@NotNull MVCoreConfig config, @NotNull LocationManipulation locationManipulation) { + this.config = config; this.locationManipulation = locationManipulation; } - public boolean playerCanSpawnSafelyAt(@NotNull Location location) { - return playerCanSpawnSafelyAt(location.getBlock()); + /** + * Function to check if a block is above air. + * + * @param location The location to check. + * @return True if the block at that {@link Location} is above air. + */ + public boolean isBlockAboveAir(Location location) { + return location.getBlock().getRelative(0, -1, 0).getType().isAir(); } - public boolean playerCanSpawnSafelyAt(@NotNull Block block) { + /** + * Checks if an entity would be on track at the specified {@link Location}. + * + * @param location The location to check. + * @return True if an entity would be on tracks at the specified {@link Location}. + */ + public boolean isEntityOnTrack(Location location) { + return location.getBlock().getBlockData() instanceof Rail; + } + + /** + * Gets the location of the highest spawnable block (i.e. y-axis) at the specified {@link Location}. + * + * @param location The location + * @return The location if found, null of all blocks are unsafe. + */ + public Location getTopBlock(Location location) { + Location check = location.clone(); + int maxHeight = Option.of(location.getWorld()).map(World::getMaxHeight).getOrElse(127); + check.setY(maxHeight); + while (check.getY() > 0) { + if (canSpawnAtLocationSafely(check)) { + return check; + } + check.setY(check.getY() - 1); + } + return null; + } + + /** + * Gets the location of the lowest spawnable block (i.e. y-axis) at the specified {@link Location}. + * + * @param location The location + * @return The location if found, null of all blocks are unsafe. + */ + public Location getBottomBlock(Location location) { + Location check = location.clone(); + int minHeight = Option.of(location.getWorld()).map(World::getMinHeight).getOrElse(0); + check.setY(minHeight); + while (check.getY() < 127) { // SUPPRESS CHECKSTYLE: MagicNumberCheck + if (canSpawnAtLocationSafely(check)) { + return check; + } + check.setY(check.getY() + 1); + } + return null; + } + + /** + * Checks if the specified {@link Minecart} can spawn safely. + * + * @param cart The {@link Minecart}. + * @return True if the minecart can spawn safely. + */ + public boolean canSpawnCartSafely(Minecart cart) { + if (isBlockAboveAir(cart.getLocation())) { + return true; + } + return isEntityOnTrack(locationManipulation.getNextBlock(cart)); + } + + /** + * Checks if the specified {@link Vehicle} can spawn safely. + * + * @param vehicle The {@link Vehicle}. + * @return True if the vehicle can spawn safely. + */ + public boolean canSpawnVehicleSafely(Vehicle vehicle) { + return isBlockAboveAir(vehicle.getLocation()); + } + + /** + * This function checks whether the block at the coordinates given is safe or not by checking for Lava/Fire/Air + * etc. This also ensures there is enough space for a player to spawn! + * + * @param location The {@link Location} + * @return Whether the player can spawn safely at the given {@link Location} + */ + public boolean canSpawnAtLocationSafely(@NotNull Location location) { + return canSpawnAtBlockSafely(location.getBlock()); + } + + /** + * This function checks whether the block at the coordinates given is safe or not by checking for Lava/Fire/Air + * etc. This also ensures there is enough space for a player to spawn! + * + * @param block The {@link Block} + * @return Whether the player can spawn safely at the given {@link Location} + */ + public boolean canSpawnAtBlockSafely(@NotNull Block block) { Logging.finest("Checking spawn safety for location: %s, %s, %s", block.getX(), block.getY(), block.getZ()); if (isUnsafeSpawnBody(block)) { // Player body will be stuck in solid @@ -60,8 +157,8 @@ public class AdvancedBlockSafety { /** * Player's body must be in non-solid block that is non-harming. * - * @param block - * @return + * @param block The block + * @return True if the block is unsafe */ private boolean isUnsafeSpawnBody(@NotNull Block block) { Material blockMaterial = block.getType(); @@ -71,8 +168,8 @@ public class AdvancedBlockSafety { /** * Player must stand on solid ground, or water that is only 1 block deep to prevent drowning. * - * @param block - * @return + * @param block The block + * @return True if the block is unsafe */ private boolean isUnsafeSpawnPlatform(@NotNull Block block) { return !block.getType().isSolid() || isDeepWater(block); @@ -81,8 +178,8 @@ public class AdvancedBlockSafety { /** * Water that is 2 or more block deep * - * @param block - * @return + * @param block The block + * @return True if the block is unsafe */ private boolean isDeepWater(@NotNull Block block) { if (block.getType() != Material.WATER) { @@ -92,26 +189,30 @@ public class AdvancedBlockSafety { } /** + * Finds the closest possible safe location around the given location with the configured search radius. * - * - * @param location - * @return The safe location, or null + * @param location The target location to find + * @return The safe location if found, otherwise null. */ @Nullable - public Location adjustSafeSpawnLocation(@NotNull Location location) { - return adjustSafeSpawnLocation(location, DEFAULT_HORIZONTAL_RANGE, DEFAULT_VERTICAL_RANGE); + public Location findSafeSpawnLocation(@NotNull Location location) { + return findSafeSpawnLocation( + location, + config.getSafeLocationHorizontalSearchRadius(), + config.getSafeLocationVerticalSearchRadius()); } /** + * Finds the closest possible safe location around the given location. * - * @param location - * @param horizontalRange - * @param verticalRange - * @return The safe location, or null + * @param location The target location to find + * @param horizontalRange The radius around x,z of given location to search. + * @param verticalRange The height of how far up and down to search. + * @return The safe location if found, otherwise null. */ @Nullable - public Location adjustSafeSpawnLocation(@NotNull Location location, int horizontalRange, int verticalRange) { - Block safeBlock = adjustSafeSpawnBlock(location.getBlock(), horizontalRange, verticalRange); + public Location findSafeSpawnLocation(@NotNull Location location, int horizontalRange, int verticalRange) { + Block safeBlock = findSafeSpawnBlock(location.getBlock(), horizontalRange, verticalRange); if (safeBlock == null) { return null; } @@ -125,24 +226,29 @@ public class AdvancedBlockSafety { } /** + * Finds the closest possible location around the given block with the configured search radius. * - * @param block - * @return The safe block, or null + * @param block The target block to find + * @return The safe block if found, otherwise null. */ @Nullable - public Block adjustSafeSpawnBlock(@NotNull Block block) { - return adjustSafeSpawnBlock(block, DEFAULT_HORIZONTAL_RANGE, DEFAULT_VERTICAL_RANGE); + public Block findSafeSpawnBlock(@NotNull Block block) { + return findSafeSpawnBlock( + block, + config.getSafeLocationHorizontalSearchRadius(), + config.getSafeLocationVerticalSearchRadius()); } /** + * Finds the closest possible location around the given block. * - * @param block - * @param horizontalRange - * @param verticalRange - * @return The safe block, or null + * @param block The target block to find + * @param horizontalRange The radius around x,z of given block to search. + * @param verticalRange The height of how far up and down to search. + * @return The safe block if found, otherwise null. */ @Nullable - public Block adjustSafeSpawnBlock(@NotNull Block block, int horizontalRange, int verticalRange) { + public Block findSafeSpawnBlock(@NotNull Block block, int horizontalRange, int verticalRange) { Block searchResult = searchAroundXZ(block, horizontalRange); if (searchResult != null) { return searchResult; @@ -169,13 +275,13 @@ public class AdvancedBlockSafety { /** * Search a square from n - radius to n + radius for both x and z * - * @param block - * @param radius + * @param block The block to be relative to + * @param radius The number of blocks +/- x and z to search * @return The safe block, or null */ @Nullable private Block searchAroundXZ(Block block, int radius) { - if (playerCanSpawnSafelyAt(block)) { + if (canSpawnAtBlockSafely(block)) { return block; } for (int r = 1; r <= radius; r++) { @@ -211,23 +317,23 @@ public class AdvancedBlockSafety { @Nullable private Block searchPlusMinusPermutation(Block block, int x, int z) { Block relative = block.getRelative(-x, 0, -z); - if (playerCanSpawnSafelyAt(relative)) { + if (canSpawnAtBlockSafely(relative)) { return relative; } if (z != 0) { relative = block.getRelative(-x, 0, z); - if (playerCanSpawnSafelyAt(relative)) { + if (canSpawnAtBlockSafely(relative)) { return relative; } } if (x != 0) { relative = block.getRelative(x, 0, -z); - if (playerCanSpawnSafelyAt(relative)) { + if (canSpawnAtBlockSafely(relative)) { return relative; } if (z != 0) { relative = block.getRelative(x, 0, z); - if (playerCanSpawnSafelyAt(relative)) { + if (canSpawnAtBlockSafely(relative)) { return relative; } } @@ -235,37 +341,54 @@ public class AdvancedBlockSafety { return null; } - public boolean isBlockAboveAir(Location location) { - return location.getBlock().getRelative(0, -1, 0).getType().isAir(); - } - /** + * Finds a portal-block next to the specified {@link Location}. * - * @param location - * @return + * @param location The {@link Location} + * @return The next portal-block's {@link Location} if found, otherwise null. */ - public boolean isEntityOnTrack(Location location) { - return location.getBlock().getBlockData() instanceof Rail; - } - - /** - * - * @param cart - * @return - */ - public boolean canSpawnCartSafely(Minecart cart) { - if (isBlockAboveAir(cart.getLocation())) { - return true; + @Nullable + public Location findPortalBlockNextTo(Location location) { + if (location.getWorld() == null) { + return null; } - return isEntityOnTrack(locationManipulation.getNextBlock(cart)); + Block b = location.getWorld().getBlockAt(location); + Location foundLocation = null; + if (b.getType() == Material.NETHER_PORTAL) { + return location; + } + if (b.getRelative(BlockFace.NORTH).getType() == Material.NETHER_PORTAL) { + foundLocation = getCloserBlock(location, b.getRelative(BlockFace.NORTH).getLocation(), foundLocation); + } + if (b.getRelative(BlockFace.SOUTH).getType() == Material.NETHER_PORTAL) { + foundLocation = getCloserBlock(location, b.getRelative(BlockFace.SOUTH).getLocation(), foundLocation); + } + if (b.getRelative(BlockFace.EAST).getType() == Material.NETHER_PORTAL) { + foundLocation = getCloserBlock(location, b.getRelative(BlockFace.EAST).getLocation(), foundLocation); + } + if (b.getRelative(BlockFace.WEST).getType() == Material.NETHER_PORTAL) { + foundLocation = getCloserBlock(location, b.getRelative(BlockFace.WEST).getLocation(), foundLocation); + } + return foundLocation; } - /** - * - * @param vehicle - * @return - */ - public boolean canSpawnVehicleSafely(Vehicle vehicle) { - return isBlockAboveAir(vehicle.getLocation()); + private Location getCloserBlock(Location source, Location blockA, Location blockB) { + // If B wasn't given, return a. + if (blockB == null) { + return blockA; + } + // Center our calculations + blockA.add(.5, 0, .5); + blockB.add(.5, 0, .5); + + // Retrieve the distance to the normalized blocks + double testA = source.distance(blockA); + double testB = source.distance(blockB); + + // Compare and return + if (testA <= testB) { + return blockA; + } + return blockB; } } diff --git a/src/main/java/org/mvplugins/multiverse/core/teleportation/AsyncSafetyTeleporter.java b/src/main/java/org/mvplugins/multiverse/core/teleportation/AsyncSafetyTeleporter.java index 24f3fb05..618335bf 100644 --- a/src/main/java/org/mvplugins/multiverse/core/teleportation/AsyncSafetyTeleporter.java +++ b/src/main/java/org/mvplugins/multiverse/core/teleportation/AsyncSafetyTeleporter.java @@ -8,7 +8,6 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jvnet.hk2.annotations.Service; -import org.mvplugins.multiverse.core.api.BlockSafety; import org.mvplugins.multiverse.core.destination.DestinationInstance; /** diff --git a/src/main/java/org/mvplugins/multiverse/core/teleportation/AsyncSafetyTeleporterAction.java b/src/main/java/org/mvplugins/multiverse/core/teleportation/AsyncSafetyTeleporterAction.java index da7aca73..8f3150ac 100644 --- a/src/main/java/org/mvplugins/multiverse/core/teleportation/AsyncSafetyTeleporterAction.java +++ b/src/main/java/org/mvplugins/multiverse/core/teleportation/AsyncSafetyTeleporterAction.java @@ -145,7 +145,7 @@ public class AsyncSafetyTeleporterAction { if (!this.checkSafety) { return Attempt.success(location); } - Location safeLocation = blockSafety.adjustSafeSpawnLocation(location); + Location safeLocation = blockSafety.findSafeSpawnLocation(location); if (safeLocation == null) { return Attempt.failure(TeleportFailureReason.UNSAFE_LOCATION); } diff --git a/src/main/java/org/mvplugins/multiverse/core/teleportation/SimpleBlockSafety.java b/src/main/java/org/mvplugins/multiverse/core/teleportation/SimpleBlockSafety.java index 8cfe72a5..6308ed31 100644 --- a/src/main/java/org/mvplugins/multiverse/core/teleportation/SimpleBlockSafety.java +++ b/src/main/java/org/mvplugins/multiverse/core/teleportation/SimpleBlockSafety.java @@ -21,7 +21,6 @@ import org.bukkit.block.data.BlockData; import org.bukkit.block.data.type.Bed; import org.bukkit.entity.Minecart; import org.bukkit.entity.Vehicle; -import org.bukkit.util.Vector; import org.jetbrains.annotations.Nullable; import org.jvnet.hk2.annotations.Service; @@ -31,6 +30,7 @@ import org.mvplugins.multiverse.core.api.LocationManipulation; /** * The default-implementation of {@link BlockSafety}. */ +@Deprecated @Service public class SimpleBlockSafety implements BlockSafety { private static final int DEFAULT_TOLERANCE = 6; diff --git a/src/main/java/org/mvplugins/multiverse/core/world/LoadedMultiverseWorld.java b/src/main/java/org/mvplugins/multiverse/core/world/LoadedMultiverseWorld.java index 4b2bad92..572fea29 100644 --- a/src/main/java/org/mvplugins/multiverse/core/world/LoadedMultiverseWorld.java +++ b/src/main/java/org/mvplugins/multiverse/core/world/LoadedMultiverseWorld.java @@ -13,8 +13,8 @@ import org.bukkit.entity.Player; import org.bukkit.generator.BiomeProvider; import org.jetbrains.annotations.NotNull; -import org.mvplugins.multiverse.core.api.BlockSafety; import org.mvplugins.multiverse.core.api.LocationManipulation; +import org.mvplugins.multiverse.core.teleportation.AdvancedBlockSafety; import org.mvplugins.multiverse.core.world.config.NullLocation; import org.mvplugins.multiverse.core.world.config.SpawnLocation; import org.mvplugins.multiverse.core.world.config.WorldConfig; @@ -23,18 +23,16 @@ import org.mvplugins.multiverse.core.world.config.WorldConfig; * Extension of {@link MultiverseWorld} that represents a world that is currently loaded with bukkit world object. */ public class LoadedMultiverseWorld extends MultiverseWorld { - private static final int SPAWN_LOCATION_SEARCH_TOLERANCE = 16; - private static final int SPAWN_LOCATION_SEARCH_RADIUS = 16; private final UUID worldUid; - private final BlockSafety blockSafety; + private final AdvancedBlockSafety blockSafety; private final LocationManipulation locationManipulation; LoadedMultiverseWorld( @NotNull World world, @NotNull WorldConfig worldConfig, - @NotNull BlockSafety blockSafety, + @NotNull AdvancedBlockSafety blockSafety, @NotNull LocationManipulation locationManipulation) { super(world.getName(), worldConfig); this.worldUid = world.getUID(); @@ -68,7 +66,7 @@ public class LoadedMultiverseWorld extends MultiverseWorld { Location location = world.getSpawnLocation(); // Verify that location was safe - if (blockSafety.playerCanSpawnHereSafely(location)) { + if (blockSafety.canSpawnAtLocationSafely(location)) { return location; } @@ -83,8 +81,7 @@ public class LoadedMultiverseWorld extends MultiverseWorld { // The location is not safe, so we need to find a better one. Logging.warning("Spawn location from world.dat file was unsafe. Adjusting..."); Logging.warning("Original Location: " + locationManipulation.strCoordsRaw(location)); - Location newSpawn = blockSafety.getSafeLocation(location, - SPAWN_LOCATION_SEARCH_TOLERANCE, SPAWN_LOCATION_SEARCH_RADIUS); + Location newSpawn = blockSafety.findSafeSpawnLocation(location); // I think we could also do this, as I think this is what Notch does. // Not sure how it will work in the nether... //Location newSpawn = this.spawnLocation.getWorld().getHighestBlockAt(this.spawnLocation).getLocation(); diff --git a/src/main/java/org/mvplugins/multiverse/core/world/WorldManager.java b/src/main/java/org/mvplugins/multiverse/core/world/WorldManager.java index 2a7912d8..83785537 100644 --- a/src/main/java/org/mvplugins/multiverse/core/world/WorldManager.java +++ b/src/main/java/org/mvplugins/multiverse/core/world/WorldManager.java @@ -27,9 +27,9 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jvnet.hk2.annotations.Service; -import org.mvplugins.multiverse.core.api.BlockSafety; import org.mvplugins.multiverse.core.api.LocationManipulation; import org.mvplugins.multiverse.core.event.MVWorldDeleteEvent; +import org.mvplugins.multiverse.core.teleportation.AdvancedBlockSafety; import org.mvplugins.multiverse.core.utils.message.MessageReplacement; import org.mvplugins.multiverse.core.utils.result.Attempt; import org.mvplugins.multiverse.core.utils.result.FailureReason; @@ -76,7 +76,7 @@ public class WorldManager { private final GeneratorProvider generatorProvider; private final PlayerWorldTeleporter playerWorldActions; private final FilesManipulator filesManipulator; - private final BlockSafety blockSafety; + private final AdvancedBlockSafety blockSafety; private final LocationManipulation locationManipulation; private final PluginManager pluginManager; @@ -87,7 +87,7 @@ public class WorldManager { @NotNull GeneratorProvider generatorProvider, @NotNull PlayerWorldTeleporter playerWorldActions, @NotNull FilesManipulator filesManipulator, - @NotNull BlockSafety blockSafety, + @NotNull AdvancedBlockSafety blockSafety, @NotNull LocationManipulation locationManipulation, @NotNull PluginManager pluginManager) { this.pluginManager = pluginManager; From 6fb620cac639f98769ed52d315b8089c2b6ff4f7 Mon Sep 17 00:00:00 2001 From: Ben Woo <30431861+benwoo1110@users.noreply.github.com> Date: Thu, 16 Jan 2025 09:39:38 +0800 Subject: [PATCH 5/6] Fix failing tests caused by new class and config changes --- .../org/mvplugins/multiverse/core/inject/InjectionTest.kt | 6 +++--- src/test/resources/fresh_config.yml | 2 ++ 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/test/java/org/mvplugins/multiverse/core/inject/InjectionTest.kt b/src/test/java/org/mvplugins/multiverse/core/inject/InjectionTest.kt index eb9f88d3..c4c7c4e3 100644 --- a/src/test/java/org/mvplugins/multiverse/core/inject/InjectionTest.kt +++ b/src/test/java/org/mvplugins/multiverse/core/inject/InjectionTest.kt @@ -10,6 +10,7 @@ import org.mvplugins.multiverse.core.config.MVCoreConfig import org.mvplugins.multiverse.core.destination.Destination import org.mvplugins.multiverse.core.economy.MVEconomist import org.mvplugins.multiverse.core.listeners.* +import org.mvplugins.multiverse.core.teleportation.AdvancedBlockSafety import org.mvplugins.multiverse.core.teleportation.AsyncSafetyTeleporter import org.mvplugins.multiverse.core.teleportation.SimpleBlockSafety import org.mvplugins.multiverse.core.teleportation.SimpleLocationManipulation @@ -34,9 +35,8 @@ class InjectionTest : TestWithMockBukkit() { } @Test - fun `BlockSafety is available as a service`() { - assertNotNull(serviceLocator.getActiveService(BlockSafety::class.java)) - assertNotNull(serviceLocator.getActiveService(SimpleBlockSafety::class.java)) + fun `AdvancedBlockSafety is available as a service`() { + assertNotNull(serviceLocator.getActiveService(AdvancedBlockSafety::class.java)) } @Test diff --git a/src/test/resources/fresh_config.yml b/src/test/resources/fresh_config.yml index f7693c63..73aec133 100644 --- a/src/test/resources/fresh_config.yml +++ b/src/test/resources/fresh_config.yml @@ -7,6 +7,8 @@ teleport: use-finer-teleport-permissions: true concurrent-teleport-limit: 50 teleport-intercept: true + safe-location-horizontal-search-radius: 3 + safe-location-vertical-search-radius: 3 spawn: first-spawn-override: true From d83d6c648320400ba7ccb65f6e2664d427533ae1 Mon Sep 17 00:00:00 2001 From: Ben Woo <30431861+benwoo1110@users.noreply.github.com> Date: Thu, 16 Jan 2025 09:42:40 +0800 Subject: [PATCH 6/6] Improve safe location search config comments --- .../multiverse/core/config/MVCoreConfigNodes.java | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/mvplugins/multiverse/core/config/MVCoreConfigNodes.java b/src/main/java/org/mvplugins/multiverse/core/config/MVCoreConfigNodes.java index f61c075a..5118a89b 100644 --- a/src/main/java/org/mvplugins/multiverse/core/config/MVCoreConfigNodes.java +++ b/src/main/java/org/mvplugins/multiverse/core/config/MVCoreConfigNodes.java @@ -132,14 +132,18 @@ class MVCoreConfigNodes { final ConfigNode SAFE_LOCATION_HORIZONTAL_SEARCH_RADIUS = node(ConfigNode.builder("teleport.safe-location-horizontal-search-radius", Integer.class) .comment("") - .comment("Sets the horizontal search radius for finding a safe location to teleport to.") + .comment("Sets the horizontal (x and z-axis) search radius for finding a safe location to teleport to.") + .comment("Increasing this value will widen the search area at the cost of performance.") + .comment("To disable, set to 0.") .defaultValue(3) .name("safe-location-horizontal-search-radius") .build()); final ConfigNode SAFE_LOCATION_VERTICAL_SEARCH_RADIUS = node(ConfigNode.builder("teleport.safe-location-vertical-search-radius", Integer.class) .comment("") - .comment("Sets the vertical search radius for finding a safe location to teleport to.") + .comment("Sets the vertical (y-axis) search radius for finding a safe location to teleport to.") + .comment("Increasing this value will widen the search area at the cost of performance.") + .comment("To disable, set to 0.") .defaultValue(3) .name("safe-location-vertical-search-radius") .build());