From 4e7b78832aac9090fa20a0200489dddefe11c216 Mon Sep 17 00:00:00 2001 From: tastybento Date: Tue, 2 Feb 2021 17:19:22 -0800 Subject: [PATCH] Nether portals (#1597) * Add WorldSettings methods for portals * Vanilla portals option added. Currently defaulted on for testing. Ultimately, the game mode config can decide if the vanilla portal is used or not. Note that the end platform is just a set of obsidian blocks. * Reduces search radius when close to island edge * Adds and fixes tests * Use EntityPortalEnterEvent instead of PlayerMoveEvent * Removed duplication between nether and end portalling * Code clean up * Single event handler for nether and end. * Created new PlayerEntityPortalEvent class to enable entity teleports Unfortunately, PlayerPortalEvent and EntityPortalEvent are not fraternal classes so there's no way to apply code to both except via this abstraction class. Tests fail. * Places end portal always in the same place. * Teleport entites to nether or end Identify the teleport cause manually because there is no method. Teleports to the End happen but seem to be slightly different locations. Some entities will disappear, others will stick around. I don't know why. * Put defaults back to false. * Create end spawn point to default point when not making end islands * Fixed PortalTeleportationListener tests. * Updated since tag --- .../api/configuration/WorldSettings.java | 20 +- .../listeners/PlayerEntityPortalEvent.java | 160 ++++++ .../PortalTeleportationListener.java | 462 +++++++++++------- .../PortalTeleportationListenerTest.java | 294 +++++------ 4 files changed, 622 insertions(+), 314 deletions(-) create mode 100644 src/main/java/world/bentobox/bentobox/listeners/PlayerEntityPortalEvent.java diff --git a/src/main/java/world/bentobox/bentobox/api/configuration/WorldSettings.java b/src/main/java/world/bentobox/bentobox/api/configuration/WorldSettings.java index 272b1b5d7..af3dbbc9a 100644 --- a/src/main/java/world/bentobox/bentobox/api/configuration/WorldSettings.java +++ b/src/main/java/world/bentobox/bentobox/api/configuration/WorldSettings.java @@ -316,7 +316,7 @@ public interface WorldSettings extends ConfigObject { */ @NonNull List getOnLeaveCommands(); - + /** * Returns a list of commands that should be executed when the player respawns after death if {@link Flags#ISLAND_RESPAWN} is true.
* @return a list of commands. @@ -537,4 +537,22 @@ public interface WorldSettings extends ConfigObject { { return "create"; } + + /** + * Make a nether portal when teleporting to the nether through an overworld portal + * @return true if a portal should be made + * @since 1.16.0 + */ + default boolean isMakeNetherPortals() { + return false; + } + + /** + * Make an end portal when teleporting to the end through an end portal + * @return true if a portal should be made + * @since 1.16.0 + */ + default boolean isMakeEndPortals() { + return false; + } } diff --git a/src/main/java/world/bentobox/bentobox/listeners/PlayerEntityPortalEvent.java b/src/main/java/world/bentobox/bentobox/listeners/PlayerEntityPortalEvent.java new file mode 100644 index 000000000..ed0cff519 --- /dev/null +++ b/src/main/java/world/bentobox/bentobox/listeners/PlayerEntityPortalEvent.java @@ -0,0 +1,160 @@ +package world.bentobox.bentobox.listeners; + +import java.util.Optional; + +import org.bukkit.Location; +import org.bukkit.World; +import org.bukkit.entity.Entity; +import org.bukkit.event.entity.EntityPortalEvent; +import org.bukkit.event.player.PlayerPortalEvent; +import org.eclipse.jdt.annotation.NonNull; +import org.eclipse.jdt.annotation.Nullable; + +import world.bentobox.bentobox.BentoBox; +import world.bentobox.bentobox.database.objects.Island; + +/** + * Abstracts PlayerPortalEvent and EntityPortalEvent + * @author tastybento + * + */ +public class PlayerEntityPortalEvent { + + private final EntityPortalEvent epe; + private final PlayerPortalEvent ppe; + + /** + * Create a hybrid PlayerEntityPortalEvent + * @param epe - EntityPortalEvent + */ + public PlayerEntityPortalEvent(EntityPortalEvent epe) { + this.ppe = null; + this.epe = epe; + } + + /** + * Create a hybrid PlayerEntityPortalEvent + * @param ppe - PlayerPortalEvent + */ + public PlayerEntityPortalEvent(PlayerPortalEvent ppe) { + this.ppe = ppe; + this.epe = null; + } + + /** + * Returns whether the server will attempt to create a destination portal or not. + * Only applicable to {@link PlayerPortalEvent} + * @return whether there should create be a destination portal created + */ + public boolean getCanCreatePortal() { + return epe == null ? ppe.getCanCreatePortal() : false; + } + + /** + * Returns the entity involved in this event + * @return Entity who is involved in this event + */ + @NonNull + public Entity getEntity() { + return epe == null ? ppe.getPlayer() : epe.getEntity(); + } + + /** + * Gets the location this player moved from + * @return Location the player or entity moved from + */ + @NonNull + public Location getFrom() { + return epe == null ? ppe.getFrom() : epe.getFrom(); + } + + /** + * Gets the location this player moved to + * @return Location the player moved to + */ + @Nullable + public Location getTo() { + return epe == null ? ppe.getTo() : epe.getTo(); + } + + /** + * @return true if constructed with an {@link EntityPortalEvent} + */ + public boolean isEntityPortalEvent() { + return epe != null; + } + + /** + * @return true if constructed with an {@link PlayerPortalEvent} + */ + public boolean isPlayerPortalEvent() { + return ppe != null; + } + + /** + * Sets the cancellation state of this event. A cancelled event will not be executed in the server, but will still pass to other plugins + * If a move or teleport event is cancelled, the player will be moved or teleported back to the Location as defined by getFrom(). This will not fire an event + * Specified by: setCancelled(...) in Cancellable + * @param cancel true if you wish to cancel this event + */ + public void setCancelled(boolean cancel) { + if (epe == null) { + ppe.setCancelled(cancel); + } else { + epe.setCancelled(cancel); + } + } + + /** + * Sets whether the server should attempt to create a destination portal or not. + * Only applicable to {@link PlayerPortalEvent} + * @param canCreatePortal Sets whether there should be a destination portal created + */ + public void setCanCreatePortal(boolean canCreatePortal) { + if (ppe != null) { + ppe.setCanCreatePortal(canCreatePortal); + } + + } + + /** + * Set the Block radius to search in for available portals. + * @param searchRadius the radius in which to search for a portal from the location + */ + public void setSearchRadius(int searchRadius) { + if (epe == null) { + ppe.setSearchRadius(searchRadius); + } else { + epe.setSearchRadius(searchRadius); + } + } + + /** + * Sets the location that this player will move to + * @param to New Location this player or entity will move to + */ + public void setTo(Location to) { + if (epe == null) { + ppe.setTo(to); + } else { + epe.setTo(to); + } + } + + /** + * Get island at the from location + * @return optional island at from location + */ + public Optional getIsland() { + return BentoBox.getInstance().getIslands().getProtectedIslandAt(getFrom()); + } + + /** + * Get the from world + * @return from world + */ + @Nullable + public World getWorld() { + return getFrom().getWorld(); + } +} diff --git a/src/main/java/world/bentobox/bentobox/listeners/PortalTeleportationListener.java b/src/main/java/world/bentobox/bentobox/listeners/PortalTeleportationListener.java index adce11c2d..d3af64304 100644 --- a/src/main/java/world/bentobox/bentobox/listeners/PortalTeleportationListener.java +++ b/src/main/java/world/bentobox/bentobox/listeners/PortalTeleportationListener.java @@ -1,5 +1,6 @@ package world.bentobox.bentobox.listeners; +import java.util.Arrays; import java.util.HashSet; import java.util.Optional; import java.util.Set; @@ -10,12 +11,14 @@ import org.bukkit.Location; import org.bukkit.Material; import org.bukkit.World; import org.bukkit.World.Environment; +import org.bukkit.block.BlockFace; +import org.bukkit.entity.EntityType; import org.bukkit.entity.Player; import org.bukkit.event.EventHandler; import org.bukkit.event.EventPriority; import org.bukkit.event.Listener; +import org.bukkit.event.entity.EntityPortalEnterEvent; import org.bukkit.event.entity.EntityPortalEvent; -import org.bukkit.event.player.PlayerMoveEvent; import org.bukkit.event.player.PlayerPortalEvent; import org.bukkit.event.player.PlayerTeleportEvent; import org.bukkit.event.player.PlayerTeleportEvent.TeleportCause; @@ -23,6 +26,7 @@ import org.bukkit.util.Vector; import org.eclipse.jdt.annotation.NonNull; import world.bentobox.bentobox.BentoBox; +import world.bentobox.bentobox.api.addons.GameModeAddon; import world.bentobox.bentobox.blueprints.Blueprint; import world.bentobox.bentobox.blueprints.BlueprintPaster; import world.bentobox.bentobox.blueprints.dataobjects.BlueprintBundle; @@ -57,19 +61,23 @@ public class PortalTeleportationListener implements Listener { * @param e */ @EventHandler(priority = EventPriority.NORMAL, ignoreCancelled = true) - public void onPlayerPortal(PlayerMoveEvent e) { - UUID uuid = e.getPlayer().getUniqueId(); - if (inPortal.contains(uuid) || !plugin.getIWM().inWorld(Util.getWorld(e.getFrom().getWorld()))) { + public void onPlayerPortal(EntityPortalEnterEvent e) { + if (!(e.getEntity() instanceof Player)) { return; } - if (!Bukkit.getAllowNether() && e.getPlayer().getLocation().getBlock().getType().equals(Material.NETHER_PORTAL)) { + Material type = e.getLocation().getBlock().getType(); + UUID uuid = e.getEntity().getUniqueId(); + if (inPortal.contains(uuid) || !plugin.getIWM().inWorld(Util.getWorld(e.getLocation().getWorld()))) { + return; + } + if (!Bukkit.getAllowNether() && type.equals(Material.NETHER_PORTAL)) { inPortal.add(uuid); // Schedule a time Bukkit.getScheduler().runTaskLater(plugin, () -> { // Check again if still in portal - if (e.getPlayer().getLocation().getBlock().getType().equals(Material.NETHER_PORTAL)) { - PlayerPortalEvent en = new PlayerPortalEvent(e.getPlayer(), e.getPlayer().getLocation(), null, TeleportCause.NETHER_PORTAL, 0, false, 0); - if (!this.onNetherPortal(en)) { + if (type.equals(Material.NETHER_PORTAL)) { + PlayerPortalEvent en = new PlayerPortalEvent((Player)e.getEntity(), e.getLocation(), null, TeleportCause.NETHER_PORTAL, 0, false, 0); + if (!this.onIslandPortal(en)) { // Failed inPortal.remove(uuid); } @@ -79,9 +87,16 @@ public class PortalTeleportationListener implements Listener { }, 40); return; } - if (!Bukkit.getAllowEnd() && e.getPlayer().getLocation().getBlock().getType().equals(Material.END_PORTAL)) { - PlayerPortalEvent en = new PlayerPortalEvent(e.getPlayer(), e.getPlayer().getLocation(), null, TeleportCause.END_PORTAL, 0, false, 0); - this.onEndIslandPortal(en); + // End portals are instant transfer + if (!Bukkit.getAllowEnd() && (type.equals(Material.END_PORTAL) || type.equals(Material.END_GATEWAY))) { + PlayerPortalEvent en = new PlayerPortalEvent((Player)e.getEntity(), + e.getLocation(), + null, + type.equals(Material.END_PORTAL) ? TeleportCause.END_PORTAL : TeleportCause.END_GATEWAY, + 0, + false, + 0); + this.onIslandPortal(en); } } @@ -92,124 +107,52 @@ public class PortalTeleportationListener implements Listener { * @param e - event */ @EventHandler(priority = EventPriority.LOW, ignoreCancelled = true) - public void onEntityPortal(EntityPortalEvent e) { + public boolean onEntityPortal(EntityPortalEvent e) { if (plugin.getIWM().inWorld(e.getFrom())) { - // Disable entity portal transfer due to dupe glitching - e.setCancelled(true); + Optional mat = Arrays.stream(BlockFace.values()) + .map(bf -> e.getFrom().getBlock().getRelative(bf).getType()) + .filter(m -> m.equals(Material.NETHER_PORTAL) + || m.equals(Material.END_PORTAL) + || m.equals(Material.END_GATEWAY)) + .findFirst(); + if (!mat.isPresent()) { + e.setCancelled(true); + return false; + } else if (mat.get().equals(Material.NETHER_PORTAL)){ + return processPortal(new PlayerEntityPortalEvent(e), Environment.NETHER); + } else { + return processPortal(new PlayerEntityPortalEvent(e), Environment.THE_END); + } } + return false; } /** - * Handles end portals + * Handles nether or end portals * @param e - event */ @EventHandler(priority = EventPriority.HIGH, ignoreCancelled = true) - public boolean onEndIslandPortal(PlayerPortalEvent e) { - if (e.getCause() != TeleportCause.END_PORTAL) { - return false; - } - World fromWorld = e.getFrom().getWorld(); - World overWorld = Util.getWorld(fromWorld); - - if (fromWorld == null || !plugin.getIWM().inWorld(overWorld)) { - // Do nothing special + public boolean onIslandPortal(PlayerPortalEvent e) { + switch (e.getCause()) { + case END_GATEWAY: + case END_PORTAL: + return processPortal(new PlayerEntityPortalEvent(e), Environment.THE_END); + case NETHER_PORTAL: + return processPortal(new PlayerEntityPortalEvent(e), Environment.NETHER); + default: return false; } - // 1.14.4 requires explicit cancellation to prevent teleporting to the normal nether - if (!plugin.getIWM().isEndGenerate(overWorld)) { - e.setCancelled(true); - return false; - } - - // STANDARD END - if (!plugin.getIWM().isEndIslands(overWorld)) { - if (fromWorld.getEnvironment() != Environment.THE_END) { - if (Bukkit.getAllowEnd()) { - // To Standard end - e.setTo(plugin.getIWM().getEndWorld(overWorld).getSpawnLocation()); - } else { - new SafeSpotTeleport.Builder(plugin) - .entity(e.getPlayer()) - .location(plugin.getIWM().getEndWorld(overWorld).getSpawnLocation()) - .thenRun(() -> inPortal.remove(e.getPlayer().getUniqueId())) - .build(); - } - } - // From standard end - check if player has an island to go to - else if (plugin.getIslands().hasIsland(overWorld, e.getPlayer().getUniqueId()) - || plugin.getIslands().inTeam(overWorld, e.getPlayer().getUniqueId())) { - e.setCancelled(true); - plugin.getIslands().homeTeleportAsync(overWorld, e.getPlayer()); - } - // No island, so just do nothing - return false; - } - - // FROM END - // If entering an ender portal in the End. - if (fromWorld.getEnvironment() == Environment.THE_END) { - // If this is from the island nether, then go to the same vector, otherwise try island home location - Location to = plugin.getIslands().getIslandAt(e.getFrom()).map(i -> i.getSpawnPoint(Environment.NORMAL)).orElseGet(() -> e.getFrom().toVector().toLocation(overWorld)); - e.setCancelled(true); - // Else other worlds teleport to the overworld - new SafeSpotTeleport.Builder(plugin) - .entity(e.getPlayer()) - .location(to) - .portal() - .build(); - return true; - } - // TO END - - World endWorld = plugin.getIWM().getEndWorld(overWorld); - // If this is to island End, then go to the same vector, otherwise try spawn - Optional optionalIsland = plugin.getIslands().getIslandAt(e.getFrom()); - Location to = optionalIsland.map(i -> i.getSpawnPoint(Environment.THE_END)).orElseGet(() -> e.getFrom().toVector().toLocation(endWorld)); - e.setCancelled(true); - // Check if there is a missing end island - if (plugin.getIWM().isPasteMissingIslands(overWorld) - && !plugin.getIWM().isUseOwnGenerator(overWorld) - && plugin.getIWM().isEndGenerate(overWorld) - && plugin.getIWM().isEndIslands(overWorld) - && plugin.getIWM().getEndWorld(overWorld) != null - && optionalIsland.filter(i -> !i.hasEndIsland()) - .map(i -> { - // No end island present so paste the default one - pasteNewIsland(e.getPlayer(), to, i, Environment.THE_END); - return true; - }).orElse(false)) { - // We are done here - return true; - } - // Set player's velocity and fall distance to 0 - e.getPlayer().setVelocity(new Vector(0,0,0)); - e.getPlayer().setFallDistance(0); - - // Else other worlds teleport to the end - // Set player's velocity to zero one tick after cancellation - // Teleport - new SafeSpotTeleport.Builder(plugin) - .entity(e.getPlayer()) - .location(to) - .thenRun(() -> { - e.getPlayer().setVelocity(new Vector(0,0,0)); - e.getPlayer().setFallDistance(0); - }) - .build(); - return true; } /** - * Handles nether portals. + * Process the portal action * @param e - event - * @return false if teleport does not happen, true if it does + * @param env - environment that this relates to - NETHER or THE_END + * @return true if portal happens, false if not */ - @EventHandler(priority = EventPriority.HIGH, ignoreCancelled = true) // Use HIGH to allow Multiverse first shot - public boolean onNetherPortal(PlayerPortalEvent e) { - if (e.getCause() != TeleportCause.NETHER_PORTAL) { - return false; - } + private boolean processPortal(final PlayerEntityPortalEvent e, final Environment env) { + World fromWorld = e.getFrom().getWorld(); World overWorld = Util.getWorld(fromWorld); if (fromWorld == null || !plugin.getIWM().inWorld(overWorld)) { @@ -217,77 +160,262 @@ public class PortalTeleportationListener implements Listener { return false; } // 1.14.4 requires explicit cancellation to prevent teleporting to the normal nether - if (!plugin.getIWM().isNetherGenerate(overWorld)) { + if (!isGenerate(overWorld, env)) { e.setCancelled(true); return false; } - // STANDARD NETHER - if (!plugin.getIWM().isNetherIslands(overWorld)) { - if (fromWorld.getEnvironment() != Environment.NETHER) { - if (Bukkit.getAllowNether()) { - // To Standard Nether - e.setTo(plugin.getIWM().getNetherWorld(overWorld).getSpawnLocation()); - } else { - // Teleport to standard nether - new SafeSpotTeleport.Builder(plugin) - .entity(e.getPlayer()) - .location(plugin.getIWM().getNetherWorld(fromWorld).getSpawnLocation()) - .portal() - .build(); - } - } - // From standard nether - else { - e.setCancelled(true); - plugin.getIslands().homeTeleportAsync(overWorld, e.getPlayer()).thenAccept(b -> inPortal.remove(e.getPlayer().getUniqueId())); - } + // STANDARD NETHER OR END + if (!isIslands(overWorld, env)) { + handleStandardNetherOrEnd(e, fromWorld, overWorld, env); return true; } - // FROM NETHER - // If entering a nether portal in the nether, teleport to portal in overworld if there is one - if (fromWorld.getEnvironment() == Environment.NETHER) { - // If this is from the island nether, then go to the same vector, otherwise try island home location - Location to = plugin.getIslands().getIslandAt(e.getFrom()).map(i -> i.getSpawnPoint(Environment.NORMAL)).orElseGet(() -> e.getFrom().toVector().toLocation(overWorld)); - e.setCancelled(true); - // Else other worlds teleport to the nether - new SafeSpotTeleport.Builder(plugin) - .entity(e.getPlayer()) - .location(to) - .portal() - .thenRun(() -> inPortal.remove(e.getPlayer().getUniqueId())) - .build(); + // FROM NETHER OR END + // If entering a portal in the other world, teleport to a portal in overworld if there is one + if (fromWorld.getEnvironment().equals(env)) { + handleFromNetherOrEnd(e, overWorld, env); return true; } - // TO NETHER - World nether = plugin.getIWM().getNetherWorld(overWorld); - // If this is to island nether, then go to the same vector, otherwise try spawn - Optional optionalIsland = plugin.getIslands().getIslandAt(e.getFrom()); - Location to = optionalIsland.map(i -> i.getSpawnPoint(Environment.NETHER)).orElseGet(() -> e.getFrom().toVector().toLocation(nether)); - e.setCancelled(true); + // TO NETHER OR END + World toWorld = getNetherEndWorld(overWorld, env); + // Set whether portals should be created or not + e.setCanCreatePortal(plugin.getIWM().getAddon(overWorld).map(gm -> isMakePortals(gm, env)).orElse(false)); + // Set the destination location + // If portals cannot be created, then destination is the spawn point, otherwise it's the vector + e.setTo(getTo(e, env, toWorld)); + + // Find the distance from edge of island's protection and set the search radius + e.getIsland().ifPresent(i -> setSeachRadius(e, i)); + // Check if there is an island there or not - if (plugin.getIWM().isPasteMissingIslands(overWorld) && - !plugin.getIWM().isUseOwnGenerator(overWorld) - && plugin.getIWM().isNetherGenerate(overWorld) - && plugin.getIWM().isNetherIslands(overWorld) - && plugin.getIWM().getNetherWorld(overWorld) != null - && optionalIsland.filter(i -> !i.hasNetherIsland()).map(i -> { + if (e.getEntity().getType().equals(EntityType.PLAYER) + && plugin.getIWM().isPasteMissingIslands(overWorld) + && !plugin.getIWM().isUseOwnGenerator(overWorld) + && isGenerate(overWorld, env) + && isIslands(overWorld, env) + && getNetherEndWorld(overWorld, env) != null + && e.getIsland().filter(i -> !hasPartnerIsland(i, env)).map(i -> { // No nether island present so paste the default one - pasteNewIsland(e.getPlayer(), to, i, Environment.NETHER); + e.setCancelled(true); + inPortal.remove(e.getEntity().getUniqueId()); + pasteNewIsland((Player)e.getEntity(), e.getTo(), i, env); return true; }).orElse(false)) { // All done here return true; } - // Else other worlds teleport to the nether - new SafeSpotTeleport.Builder(plugin) - .entity(e.getPlayer()) - .location(to) - .portal() - .thenRun(() -> inPortal.remove(e.getPlayer().getUniqueId())) - .build(); + if (e.getCanCreatePortal()) { + // Let the server teleport + inPortal.remove(e.getEntity().getUniqueId()); + return true; + } + if (env.equals(Environment.THE_END)) { + // Prevent death from hitting the ground + e.getEntity().setVelocity(new Vector(0,0,0)); + e.getEntity().setFallDistance(0); + } + // If there is a portal to go to already, then the player will go there + Bukkit.getScheduler().runTask(plugin, () -> { + if (!e.getEntity().getWorld().equals(toWorld)) { + // Else manually teleport entity + new SafeSpotTeleport.Builder(plugin) + .entity(e.getEntity()) + .location(e.getTo()) + .portal() + .thenRun(() -> { + inPortal.remove(e.getEntity().getUniqueId()); + e.getEntity().setVelocity(new Vector(0,0,0)); + e.getEntity().setFallDistance(0); + }) + .build(); + } + }); return true; } + + /** + * Set the destination of this portal action + * @param e - event + * @param env - environment + * @param toWorld - to world + */ + Location getTo(PlayerEntityPortalEvent e, Environment env, World toWorld) { + // Null check - not that useful + if (e.getFrom().getWorld() == null || toWorld == null) { + return null; + } + if (!e.getCanCreatePortal()) { + // Legacy portaling + return e.getIsland().map(i -> i.getSpawnPoint(env)).orElse(e.getFrom().toVector().toLocation(toWorld)); + } + // Make portals + // For anywhere other than the end - it is the player's location that is used + if (!env.equals(Environment.THE_END)) { + return e.getFrom().toVector().toLocation(toWorld); + } + // If the-end then we want the platform to always be generated in the same place no matter where + // they enter the portal + final int x = e.getFrom().getBlockX(); + final int z = e.getFrom().getBlockZ(); + final int y = e.getFrom().getBlockY(); + int i = x; + int j = z; + int k = y; + // If the from is not a portal, then we have to find it + if (!e.getFrom().getBlock().getType().equals(Material.END_PORTAL)) { + // Find the portal - due to speed, it is possible that the player will be below or above the portal + for (k = 0; (k < e.getWorld().getMaxHeight()) && !e.getWorld().getBlockAt(x, k, z).getType().equals(Material.END_PORTAL); k++); + } + // Find the maximum x and z corner + for (; (i < x + 5) && e.getWorld().getBlockAt(i, k, z).getType().equals(Material.END_PORTAL); i++); + for (; (j < z + 5) && e.getWorld().getBlockAt(x, k, j).getType().equals(Material.END_PORTAL); j++); + return new Location(toWorld, i, k, j); + } + + + /** + * Check if vanilla portals should be used + * @param gm - game mode + * @param env - environment + * @return true or false + */ + private boolean isMakePortals(GameModeAddon gm, Environment env) { + return env.equals(Environment.NETHER) ? gm.getWorldSettings().isMakeNetherPortals() : gm.getWorldSettings().isMakeEndPortals(); + } + + /** + * Check if nether or end are generated + * @param gm - game mode + * @param env - environment + * @return true or false + */ + private boolean isGenerate(World overWorld, Environment env) { + return env.equals(Environment.NETHER) ? plugin.getIWM().isNetherGenerate(overWorld) : plugin.getIWM().isEndGenerate(overWorld); + } + + /** + * Check if nether or end islands are generated + * @param gm - game mode + * @param env - environment + * @return true or false + */ + private boolean isIslands(World overWorld, Environment env) { + return env.equals(Environment.NETHER) ? plugin.getIWM().isNetherIslands(overWorld) : plugin.getIWM().isEndIslands(overWorld); + } + + /** + * Get the nether or end world + * @param gm - game mode + * @param env - environment + * @return nether or end world + */ + private World getNetherEndWorld(World overWorld, Environment env) { + return env.equals(Environment.NETHER) ? plugin.getIWM().getNetherWorld(overWorld) : plugin.getIWM().getEndWorld(overWorld); + } + + /** + * Check if the island has a nether or end island already + * @param gm - game mode + * @param env - environment + * @return true or false + */ + private boolean hasPartnerIsland(Island i, Environment env) { + return env.equals(Environment.NETHER) ? i.hasNetherIsland() : i.hasEndIsland(); + } + + /** + * Check if the default nether or end are allowed by the server settings + * @param gm - game mode + * @param env - environment + * @return true or false + */ + private boolean isAllowedOnServer(Environment env) { + return env.equals(Environment.NETHER) ? Bukkit.getAllowNether() : Bukkit.getAllowEnd(); + } + + /** + * Handle teleport from nether or end to overworld + * @param e - event + * @param overWorld - over world + * @param env - environment + */ + private void handleFromNetherOrEnd(PlayerEntityPortalEvent e, World overWorld, Environment env) { + // Standard portals + if (plugin.getIWM().getAddon(overWorld).map(gm -> isMakePortals(gm, env)).orElse(false)) { + e.setTo(e.getFrom().toVector().toLocation(overWorld)); + // Find distance from edge of island's protection + plugin.getIslands().getIslandAt(e.getFrom()).ifPresent(i -> setSeachRadius(e, i)); + inPortal.remove(e.getEntity().getUniqueId()); + return; + } + // Custom portals + e.setCancelled(true); + // If this is from the island nether or end, then go to the same vector, otherwise try island home location + Location to = plugin.getIslands().getIslandAt(e.getFrom()).map(i -> i.getSpawnPoint(Environment.NORMAL)).orElse(e.getFrom().toVector().toLocation(overWorld)); + e.setTo(to); + // Else other worlds teleport to the nether + new SafeSpotTeleport.Builder(plugin) + .entity(e.getEntity()) + .location(to) + .portal() + .thenRun(() -> inPortal.remove(e.getEntity().getUniqueId())) + .build(); + + } + + + /** + * Handle teleport from or to standard nether or end + * @param e + * @param fromWorld + * @param overWorld + * @param env + */ + private void handleStandardNetherOrEnd(PlayerEntityPortalEvent e, World fromWorld, World overWorld, Environment env) { + if (fromWorld.getEnvironment() != env) { + World toWorld = getNetherEndWorld(overWorld, env); + Location spawnPoint = toWorld.getSpawnLocation(); + // If spawn is set as 0,63,0 in the End then move it to 100, 50 ,0. + if (env.equals(Environment.THE_END) && spawnPoint.toVector().equals(new Vector(0,63,0))) { + // Set to the default end spawn + spawnPoint = new Location(toWorld, 100, 50, 0); + toWorld.setSpawnLocation(100, 50, 0); + } + if (isAllowedOnServer(env)) { + // To Standard Nether or end + plugin.logDebug("Spawn = " + spawnPoint); + e.setTo(spawnPoint); + } else { + plugin.logDebug("Spawn = " + spawnPoint); + // Teleport to standard nether or end + new SafeSpotTeleport.Builder(plugin) + .entity(e.getEntity()) + .location(spawnPoint) + .portal() + .build(); + } + } + // From standard nether or end + else if (e.getEntity() instanceof Player){ + e.setCancelled(true); + plugin.getIslands().homeTeleportAsync(overWorld, (Player)e.getEntity()).thenAccept(b -> inPortal.remove(e.getEntity().getUniqueId())); + } + + } + + + void setSeachRadius(PlayerEntityPortalEvent e, Island i) { + if (!i.onIsland(e.getFrom())) return; + // Find max x or max z + int x = Math.abs(i.getCenter().getBlockX() - e.getFrom().getBlockX()); + int z = Math.abs(i.getCenter().getBlockZ() - e.getFrom().getBlockZ()); + int diff = i.getProtectionRange() - Math.max(x, z); + if (diff > 0 && diff < 128) { + e.setSearchRadius(diff); + } + } + + /** * Pastes the default nether or end island and teleports the player to the island's spawn point * @param player - player to teleport after pasting diff --git a/src/test/java/world/bentobox/bentobox/listeners/PortalTeleportationListenerTest.java b/src/test/java/world/bentobox/bentobox/listeners/PortalTeleportationListenerTest.java index b6fc0afa8..9da3be7e3 100644 --- a/src/test/java/world/bentobox/bentobox/listeners/PortalTeleportationListenerTest.java +++ b/src/test/java/world/bentobox/bentobox/listeners/PortalTeleportationListenerTest.java @@ -1,11 +1,13 @@ package world.bentobox.bentobox.listeners; +import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; +import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @@ -16,9 +18,12 @@ import java.util.concurrent.CompletableFuture; import org.bukkit.Bukkit; import org.bukkit.Location; +import org.bukkit.Material; import org.bukkit.World; import org.bukkit.World.Environment; +import org.bukkit.block.Block; import org.bukkit.entity.Entity; +import org.bukkit.entity.EntityType; import org.bukkit.entity.Player; import org.bukkit.event.entity.EntityPortalEvent; import org.bukkit.event.player.PlayerPortalEvent; @@ -40,6 +45,7 @@ import org.powermock.reflect.Whitebox; import world.bentobox.bentobox.BentoBox; import world.bentobox.bentobox.Settings; import world.bentobox.bentobox.api.addons.GameModeAddon; +import world.bentobox.bentobox.api.configuration.WorldSettings; import world.bentobox.bentobox.api.user.User; import world.bentobox.bentobox.blueprints.Blueprint; import world.bentobox.bentobox.blueprints.BlueprintPaster; @@ -79,6 +85,10 @@ public class PortalTeleportationListenerTest { private BlueprintsManager bpm; @Mock private GameModeAddon gameModeAddon; + @Mock + private WorldSettings ws; + @Mock + private Player player; @Before public void setUp() throws Exception { @@ -102,7 +112,7 @@ public class PortalTeleportationListenerTest { when(iwm.getNetherSpawnRadius(any())).thenReturn(100); when(plugin.getIWM()).thenReturn(iwm); - PowerMockito.mockStatic(Util.class); + PowerMockito.mockStatic(Util.class, Mockito.RETURNS_MOCKS); when(Util.getWorld(any())).thenReturn(world); // Settings @@ -167,7 +177,11 @@ public class PortalTeleportationListenerTest { defaultBB.setBlueprint(World.Environment.THE_END, bp); when(bpm.getDefaultBlueprintBundle(any())).thenReturn(defaultBB); when(bpm.getBlueprints(any())).thenReturn(Collections.singletonMap("blueprintname", bp)); - // Paster + // World Settings + when(gameModeAddon.getWorldSettings()).thenReturn(ws); + + // Player + when(player.getType()).thenReturn(EntityType.PLAYER); } @@ -183,26 +197,26 @@ public class PortalTeleportationListenerTest { } /** - * Test method for {@link PortalTeleportationListener#onEndIslandPortal(org.bukkit.event.player.PlayerPortalEvent)}. + * Test method for {@link PortalTeleportationListener#onIslandPortal(org.bukkit.event.player.PlayerPortalEvent)}. */ @Test - public void testOnEndIslandPortalNotEnd() { + public void testonIslandPortalNotEnd() { Location from = mock(Location.class); // Teleport from world to nether when(from.getWorld()).thenReturn(world); when(from.toVector()).thenReturn(new Vector(1,2,3)); PortalTeleportationListener np = new PortalTeleportationListener(plugin); // Wrong cause - PlayerPortalEvent e = new PlayerPortalEvent(null, from, null, TeleportCause.CHORUS_FRUIT); - np.onEndIslandPortal(e); + PlayerPortalEvent e = new PlayerPortalEvent(player, from, null, TeleportCause.CHORUS_FRUIT); + np.onIslandPortal(e); assertFalse(e.isCancelled()); } /** - * Test method for {@link PortalTeleportationListener#onEndIslandPortal(org.bukkit.event.player.PlayerPortalEvent)}. + * Test method for {@link PortalTeleportationListener#onIslandPortal(org.bukkit.event.player.PlayerPortalEvent)}. */ @Test - public void testOnEndIslandPortalNoEndWorldGenerated() { + public void testonIslandPortalNoEndWorldGenerated() { Location from = mock(Location.class); // Teleport from world to end when(from.getWorld()).thenReturn(world); @@ -210,46 +224,46 @@ public class PortalTeleportationListenerTest { // No end world when(iwm.isEndGenerate(any())).thenReturn(false); PortalTeleportationListener np = new PortalTeleportationListener(plugin); - PlayerPortalEvent e = new PlayerPortalEvent(null, from, null, TeleportCause.END_PORTAL); - np.onEndIslandPortal(e); + PlayerPortalEvent e = new PlayerPortalEvent(player, from, null, TeleportCause.END_PORTAL); + np.onIslandPortal(e); assertTrue(e.isCancelled()); } /** - * Test method for {@link PortalTeleportationListener#onEndIslandPortal(org.bukkit.event.player.PlayerPortalEvent)}. + * Test method for {@link PortalTeleportationListener#onIslandPortal(org.bukkit.event.player.PlayerPortalEvent)}. */ @Test - public void testOnEndIslandPortalWrongWorld() { + public void testonIslandPortalWrongWorld() { PortalTeleportationListener np = new PortalTeleportationListener(plugin); Location loc = mock(Location.class); // Right cause, end exists, wrong world when(loc.getWorld()).thenReturn(mock(World.class)); wrongWorld(); - PlayerPortalEvent e = new PlayerPortalEvent(null, loc, null, TeleportCause.END_PORTAL); + PlayerPortalEvent e = new PlayerPortalEvent(player, loc, null, TeleportCause.END_PORTAL); when(iwm.isEndGenerate(world)).thenReturn(true); - np.onEndIslandPortal(e); + np.onIslandPortal(e); assertFalse(e.isCancelled()); } /** - * Test method for {@link PortalTeleportationListener#onEndIslandPortal(org.bukkit.event.player.PlayerPortalEvent)}. + * Test method for {@link PortalTeleportationListener#onIslandPortal(org.bukkit.event.player.PlayerPortalEvent)}. */ @Test - public void testOnEndIslandPortalNullWorld() { + public void testonIslandPortalNullWorld() { PortalTeleportationListener np = new PortalTeleportationListener(plugin); Location loc = mock(Location.class); when(loc.getWorld()).thenReturn(null); - PlayerPortalEvent e = new PlayerPortalEvent(null, loc, null, TeleportCause.END_PORTAL); - assertFalse(np.onEndIslandPortal(e)); + PlayerPortalEvent e = new PlayerPortalEvent(player, loc, null, TeleportCause.END_PORTAL); + assertFalse(np.onIslandPortal(e)); assertFalse(e.isCancelled()); } /** - * Test method for {@link PortalTeleportationListener#onEndIslandPortal(org.bukkit.event.player.PlayerPortalEvent)}. + * Test method for {@link PortalTeleportationListener#onIslandPortal(org.bukkit.event.player.PlayerPortalEvent)}. */ @Test - public void testOnEndIslandPortalHome() { + public void testonIslandPortalHome() { PortalTeleportationListener np = new PortalTeleportationListener(plugin); Location from = mock(Location.class); // Teleport from end @@ -262,27 +276,29 @@ public class PortalTeleportationListenerTest { // Right cause, end exists, right world PlayerPortalEvent e = new PlayerPortalEvent(player, from, null, TeleportCause.END_PORTAL); when(iwm.isEndGenerate(world)).thenReturn(true); - np.onEndIslandPortal(e); - assertFalse(e.isCancelled()); + // No island for player + when(im.hasIsland(any(), any(UUID.class))).thenReturn(false); + np.onIslandPortal(e); + assertTrue(e.isCancelled()); // Give player an island when(im.hasIsland(any(), any(UUID.class))).thenReturn(true); - np.onEndIslandPortal(e); + np.onIslandPortal(e); assertTrue(e.isCancelled()); - verify(im).homeTeleportAsync(any(), eq(player)); + verify(im, times(2)).homeTeleportAsync(any(), eq(player)); } /** - * Test method for {@link PortalTeleportationListener#onEndIslandPortal(org.bukkit.event.player.PlayerPortalEvent)}. + * Test method for {@link PortalTeleportationListener#onIslandPortal(org.bukkit.event.player.PlayerPortalEvent)}. */ @Test - public void testOnEndIslandPortalNonBentoBoxWorld() { + public void testonIslandPortalNonBentoBoxWorld() { when(iwm.inWorld(any(World.class))).thenReturn(false); PortalTeleportationListener np = new PortalTeleportationListener(plugin); Location from = mock(Location.class); // Teleport from nether to world when(from.getWorld()).thenReturn(mock(World.class)); - PlayerPortalEvent e = new PlayerPortalEvent(null, from, null, TeleportCause.NETHER_PORTAL); - assertFalse(np.onEndIslandPortal(e)); + PlayerPortalEvent e = new PlayerPortalEvent(player, from, null, TeleportCause.NETHER_PORTAL); + assertFalse(np.onIslandPortal(e)); // Verify assertFalse(e.isCancelled()); verify(iwm, never()).isEndGenerate(any()); @@ -295,8 +311,14 @@ public class PortalTeleportationListenerTest { public void testOnEntityPortal() { PortalTeleportationListener np = new PortalTeleportationListener(plugin); Entity ent = mock(Entity.class); + when(ent.getType()).thenReturn(EntityType.VILLAGER); + when(ent.getWorld()).thenReturn(world); Location from = mock(Location.class); - when(from.getWorld()).thenReturn(mock(World.class)); + when(from.getWorld()).thenReturn(world); + Block block = mock(Block.class); + when(from.getBlock()).thenReturn(block); + when(block.getRelative(any())).thenReturn(block); + when(block.getType()).thenReturn(Material.NETHER_PORTAL); // Not in world wrongWorld(); EntityPortalEvent e = new EntityPortalEvent(ent, from, null); @@ -307,49 +329,49 @@ public class PortalTeleportationListenerTest { when(iwm.inWorld(any(Location.class))).thenReturn(true); e = new EntityPortalEvent(ent, from, null); np.onEntityPortal(e); - assertTrue(e.isCancelled()); + assertFalse(e.isCancelled()); } /** - * Test method for {@link PortalTeleportationListener#onNetherPortal(org.bukkit.event.player.PlayerPortalEvent)}. + * Test method for {@link PortalTeleportationListener#onIslandPortal(org.bukkit.event.player.PlayerPortalEvent)}. */ @Test - public void testOnNetherPortalNotPortal() { + public void testonIslandPortalNotPortal() { PortalTeleportationListener np = new PortalTeleportationListener(plugin); - PlayerPortalEvent e = new PlayerPortalEvent(null, null, null, TeleportCause.COMMAND); - assertFalse(np.onNetherPortal(e)); + PlayerPortalEvent e = new PlayerPortalEvent(player, null, null, TeleportCause.COMMAND); + assertFalse(np.onIslandPortal(e)); } /** - * Test method for {@link PortalTeleportationListener#onNetherPortal(org.bukkit.event.player.PlayerPortalEvent)}. + * Test method for {@link PortalTeleportationListener#onIslandPortal(org.bukkit.event.player.PlayerPortalEvent)}. */ @Test - public void testOnNetherPortalWrongWorld() { + public void testonIslandPortalWrongWorldNether() { PortalTeleportationListener np = new PortalTeleportationListener(plugin); Location from = mock(Location.class); when(from.getWorld()).thenReturn(mock(World.class)); wrongWorld(); - PlayerPortalEvent e = new PlayerPortalEvent(null, from, null, TeleportCause.NETHER_PORTAL); - assertFalse(np.onNetherPortal(e)); + PlayerPortalEvent e = new PlayerPortalEvent(player, from, null, TeleportCause.NETHER_PORTAL); + assertFalse(np.onIslandPortal(e)); } /** - * Test method for {@link PortalTeleportationListener#onNetherPortal(org.bukkit.event.player.PlayerPortalEvent)}. + * Test method for {@link PortalTeleportationListener#onIslandPortal(org.bukkit.event.player.PlayerPortalEvent)}. */ @Test - public void testOnNetherPortalFromWorldToNetherIsland() { + public void testonIslandPortalFromWorldToNetherIsland() { PortalTeleportationListener np = new PortalTeleportationListener(plugin); Location from = mock(Location.class); // Teleport from world to nether when(from.getWorld()).thenReturn(world); when(from.toVector()).thenReturn(new Vector(1,2,3)); - PlayerPortalEvent e = new PlayerPortalEvent(null, from, null, TeleportCause.NETHER_PORTAL); + PlayerPortalEvent e = new PlayerPortalEvent(player, from, null, TeleportCause.NETHER_PORTAL); // Nether islands active when(iwm.isNetherIslands(any())).thenReturn(true); when(iwm.isNetherGenerate(any())).thenReturn(true); - assertTrue(np.onNetherPortal(e)); - // Verify - assertTrue(e.isCancelled()); + assertTrue(np.onIslandPortal(e)); + // Event is not canceled + assertFalse(e.isCancelled()); // If nether islands, then to = from but in nether verify(from).toVector(); // Do not go to spawn @@ -357,105 +379,52 @@ public class PortalTeleportationListenerTest { } /** - * Test method for {@link PortalTeleportationListener#onNetherPortal(org.bukkit.event.player.PlayerPortalEvent)}. + * Test method for {@link PortalTeleportationListener#onIslandPortal(org.bukkit.event.player.PlayerPortalEvent)}. */ @Test - public void testOnNetherPortalFromWorldToNetherIslandPasteBlueprintError() { + public void testonIslandPortalFromWorldToNetherIslandWithSpawnDefined() { PortalTeleportationListener np = new PortalTeleportationListener(plugin); Location from = mock(Location.class); + Location to = mock(Location.class); + when(to.getWorld()).thenReturn(world); // Teleport from world to nether when(from.getWorld()).thenReturn(world); when(from.toVector()).thenReturn(new Vector(1,2,3)); - PlayerPortalEvent e = new PlayerPortalEvent(null, from, null, TeleportCause.NETHER_PORTAL); - // Nether islands active - when(iwm.isNetherIslands(any())).thenReturn(true); - when(iwm.isNetherGenerate(any())).thenReturn(true); - // Paste - when(iwm.isPasteMissingIslands(any())).thenReturn(true); - Island isle = mock(Island.class); - when(isle.getWorld()).thenReturn(world); - when(isle.hasEndIsland()).thenReturn(false); - Optional island = Optional.of(isle ); - when(im.getIslandAt(any())).thenReturn(island); - // No bp - when(bpm.getBlueprints(any())).thenReturn(Collections.emptyMap()); - // Test - assertTrue(np.onNetherPortal(e)); - // Error - verify(plugin).logError(eq("Could not paste default island in nether or end. Is there a nether-island or end-island blueprint?")); - } - - /** - * Test method for {@link PortalTeleportationListener#onNetherPortal(org.bukkit.event.player.PlayerPortalEvent)}. - */ - @Test - public void testOnNetherPortalFromWorldToNetherIslandPasteBlueprint() { - PortalTeleportationListener np = new PortalTeleportationListener(plugin); - Location from = mock(Location.class); - // Teleport from world to nether - when(from.getWorld()).thenReturn(world); - when(from.toVector()).thenReturn(new Vector(1,2,3)); - PlayerPortalEvent e = new PlayerPortalEvent(null, from, null, TeleportCause.NETHER_PORTAL); - // Nether islands active - when(iwm.isNetherIslands(any())).thenReturn(true); - when(iwm.isNetherGenerate(any())).thenReturn(true); - // Paste - when(iwm.isPasteMissingIslands(any())).thenReturn(true); - Island isle = mock(Island.class); - when(isle.getWorld()).thenReturn(world); - when(isle.getCenter()).thenReturn(from); - when(isle.hasEndIsland()).thenReturn(false); - Optional island = Optional.of(isle ); - when(im.getIslandAt(any())).thenReturn(island); - // Test - assertTrue(np.onNetherPortal(e)); - // Error - verify(plugin, never()).logError(eq("Could not paste default island in nether or end. Is there a nether-island or end-island blueprint?")); - } - - /** - * Test method for {@link PortalTeleportationListener#onNetherPortal(org.bukkit.event.player.PlayerPortalEvent)}. - */ - @Test - public void testOnNetherPortalFromWorldToNetherIslandWithSpawnDefined() { - PortalTeleportationListener np = new PortalTeleportationListener(plugin); - Location from = mock(Location.class); - // Teleport from world to nether - when(from.getWorld()).thenReturn(world); - when(from.toVector()).thenReturn(new Vector(1,2,3)); - PlayerPortalEvent e = new PlayerPortalEvent(null, from, null, TeleportCause.NETHER_PORTAL); + PlayerPortalEvent e = new PlayerPortalEvent(player, from, to, TeleportCause.NETHER_PORTAL); // Nether islands active when(iwm.isNetherIslands(any())).thenReturn(true); when(iwm.isNetherGenerate(any())).thenReturn(true); + when(iwm.getNetherWorld(any())).thenReturn(nether); Island island = mock(Island.class); Location spawnLoc = mock(Location.class); + when(spawnLoc.getWorld()).thenReturn(world); when(island.getSpawnPoint(any())).thenReturn(spawnLoc); Optional optionalIsland = Optional.of(island); // Island exists at location when(im.getIslandAt(any())).thenReturn(optionalIsland); - assertTrue(np.onNetherPortal(e)); + assertTrue(np.onIslandPortal(e)); // Verify - assertTrue(e.isCancelled()); - // If nether islands, then to spawn location - verify(island).getSpawnPoint(eq(Environment.NETHER)); - // Do not go to from - verify(from, never()).toVector(); + assertFalse(e.isCancelled()); + // If nether islands, then to = from but in nether + verify(from).toVector(); + // Do not go to spawn + verify(nether, never()).getSpawnLocation(); } /** - * Test method for {@link PortalTeleportationListener#onNetherPortal(org.bukkit.event.player.PlayerPortalEvent)}. + * Test method for {@link PortalTeleportationListener#onIslandPortal(org.bukkit.event.player.PlayerPortalEvent)}. */ @Test - public void testOnNetherPortalFromWorldToNetherIslandWithNoSpawnDefined() { + public void testonIslandPortalFromWorldToNetherIslandWithNoSpawnDefined() { PortalTeleportationListener np = new PortalTeleportationListener(plugin); Location from = mock(Location.class); // Teleport from world to nether when(from.getWorld()).thenReturn(world); when(from.toVector()).thenReturn(new Vector(1,2,3)); - PlayerPortalEvent e = new PlayerPortalEvent(null, from, null, TeleportCause.NETHER_PORTAL); + PlayerPortalEvent e = new PlayerPortalEvent(player, from, null, TeleportCause.NETHER_PORTAL); // Nether islands active when(iwm.isNetherIslands(any())).thenReturn(true); when(iwm.isNetherGenerate(any())).thenReturn(true); @@ -467,9 +436,9 @@ public class PortalTeleportationListenerTest { when(im.getIslandAt(any())).thenReturn(optionalIsland); - assertTrue(np.onNetherPortal(e)); + assertTrue(np.onIslandPortal(e)); // Verify - assertTrue(e.isCancelled()); + assertFalse(e.isCancelled()); // If nether islands, then to = from but in nether verify(from).toVector(); // Do not go to spawn @@ -477,30 +446,30 @@ public class PortalTeleportationListenerTest { } /** - * Test method for {@link PortalTeleportationListener#onNetherPortal(org.bukkit.event.player.PlayerPortalEvent)}. + * Test method for {@link PortalTeleportationListener#onIslandPortal(org.bukkit.event.player.PlayerPortalEvent)}. */ @Test - public void testOnNetherPortalFromWorldToNetherStandard() { + public void testonIslandPortalFromWorldToNetherStandard() { PortalTeleportationListener np = new PortalTeleportationListener(plugin); Location from = mock(Location.class); // Teleport from world to nether when(from.getWorld()).thenReturn(world); when(from.toVector()).thenReturn(new Vector(1,2,3)); - PlayerPortalEvent e = new PlayerPortalEvent(null, from, null, TeleportCause.NETHER_PORTAL); + PlayerPortalEvent e = new PlayerPortalEvent(player, from, null, TeleportCause.NETHER_PORTAL); // Nether islands inactive when(iwm.isNetherIslands(any())).thenReturn(false); when(iwm.isNetherGenerate(any())).thenReturn(true); - assertTrue(np.onNetherPortal(e)); + assertTrue(np.onIslandPortal(e)); // Verify assertFalse(e.isCancelled()); } /** - * Test method for {@link PortalTeleportationListener#onNetherPortal(org.bukkit.event.player.PlayerPortalEvent)}. + * Test method for {@link PortalTeleportationListener#onIslandPortal(org.bukkit.event.player.PlayerPortalEvent)}. * @throws Exception */ @Test - public void testOnNetherPortalFromNetherStandard() throws Exception { + public void testonIslandPortalFromNetherStandard() throws Exception { PortalTeleportationListener np = new PortalTeleportationListener(plugin); Location from = mock(Location.class); // Teleport from nether to world @@ -515,26 +484,26 @@ public class PortalTeleportationListenerTest { when(iwm.isNetherGenerate(any())).thenReturn(true); // Player should be teleported to their island - assertTrue(np.onNetherPortal(e)); + assertTrue(np.onIslandPortal(e)); // Verify assertTrue(e.isCancelled()); } /** - * Test method for {@link PortalTeleportationListener#onNetherPortal(org.bukkit.event.player.PlayerPortalEvent)}. + * Test method for {@link PortalTeleportationListener#onIslandPortal(org.bukkit.event.player.PlayerPortalEvent)}. */ @Test - public void testOnNetherPortalFromNetherIsland() { + public void testonIslandPortalFromNetherIsland() { PortalTeleportationListener np = new PortalTeleportationListener(plugin); Location from = mock(Location.class); // Teleport from nether to world when(from.getWorld()).thenReturn(nether); when(from.toVector()).thenReturn(new Vector(1,2,3)); - PlayerPortalEvent e = new PlayerPortalEvent(null, from, null, TeleportCause.NETHER_PORTAL); + PlayerPortalEvent e = new PlayerPortalEvent(player, from, null, TeleportCause.NETHER_PORTAL); // Nether islands active when(iwm.isNetherIslands(any())).thenReturn(true); when(iwm.isNetherGenerate(any())).thenReturn(true); - assertTrue(np.onNetherPortal(e)); + assertTrue(np.onIslandPortal(e)); // Verify assertTrue(e.isCancelled()); // If regular nether, then to = island location @@ -543,48 +512,81 @@ public class PortalTeleportationListenerTest { } /** - * Test method for {@link PortalTeleportationListener#onNetherPortal(org.bukkit.event.player.PlayerPortalEvent)}. + * Test method for {@link PortalTeleportationListener#onIslandPortal(org.bukkit.event.player.PlayerPortalEvent)}. */ @Test - public void testOnNetherIslandPortalNullLocation() { - PortalTeleportationListener np = new PortalTeleportationListener(plugin); - Location loc = null; - PlayerPortalEvent e = new PlayerPortalEvent(null, loc, null, TeleportCause.END_PORTAL); - assertFalse(np.onNetherPortal(e)); - assertFalse(e.isCancelled()); - } - - /** - * Test method for {@link PortalTeleportationListener#onNetherPortal(org.bukkit.event.player.PlayerPortalEvent)}. - */ - @Test - public void testOnNetherPortalNullWorld() { + public void testonIslandPortalNullWorldNether() { PortalTeleportationListener np = new PortalTeleportationListener(plugin); Location from = mock(Location.class); // Teleport from nether to world when(from.getWorld()).thenReturn(null); - PlayerPortalEvent e = new PlayerPortalEvent(null, from, null, TeleportCause.NETHER_PORTAL); - assertFalse(np.onNetherPortal(e)); + PlayerPortalEvent e = new PlayerPortalEvent(player, from, null, TeleportCause.NETHER_PORTAL); + assertFalse(np.onIslandPortal(e)); // Verify assertFalse(e.isCancelled()); } /** - * Test method for {@link PortalTeleportationListener#onNetherPortal(org.bukkit.event.player.PlayerPortalEvent)}. + * Test method for {@link PortalTeleportationListener#onIslandPortal(org.bukkit.event.player.PlayerPortalEvent)}. */ @Test - public void testOnNetherPortalNonBentoBoxWorld() { + public void testonIslandPortalNonBentoBoxWorldNether() { when(iwm.inWorld(any(World.class))).thenReturn(false); PortalTeleportationListener np = new PortalTeleportationListener(plugin); Location from = mock(Location.class); // Teleport from nether to world when(from.getWorld()).thenReturn(mock(World.class)); - PlayerPortalEvent e = new PlayerPortalEvent(null, from, null, TeleportCause.NETHER_PORTAL); - assertFalse(np.onNetherPortal(e)); + PlayerPortalEvent e = new PlayerPortalEvent(player, from, null, TeleportCause.NETHER_PORTAL); + assertFalse(np.onIslandPortal(e)); // Verify assertFalse(e.isCancelled()); verify(iwm, never()).isNetherGenerate(any()); } + /** + * Test method for {@link PortalTeleportationListener#setSeachRadius(PlayerPortalEvent, Island) + */ + @Test + public void testSetSeachRadius() { + Location from = mock(Location.class); + Location to = mock(Location.class); + PlayerPortalEvent e = new PlayerPortalEvent(p, from, to); + Island island = mock(Island.class); + when(island.onIsland(any())).thenReturn(true); + Location center = mock(Location.class); + when(center.getBlockX()).thenReturn(200); + when(center.getBlockZ()).thenReturn(200); + when(island.getCenter()).thenReturn(center); + when(island.getProtectionRange()).thenReturn(200); + PortalTeleportationListener np = new PortalTeleportationListener(plugin); + when(from.getBlockZ()).thenReturn(205); + assertEquals(128, e.getSearchRadius()); + + for (int x = 200; x < 410; x++) { + when(from.getBlockX()).thenReturn(x); + np.setSeachRadius(new PlayerEntityPortalEvent(e), island); + if (x >= 400) { + assertEquals(1, e.getSearchRadius()); + } else if (x < 273) { + assertEquals(128, e.getSearchRadius()); + } else if (x < 400) { + assertEquals(400 - x, e.getSearchRadius()); + } + } + } + + /** + * Test method for {@link PortalTeleportationListener#setSeachRadius(PlayerPortalEvent, Island) + */ + @Test + public void testSetSeachRadiusNotOnIsland() { + Location from = mock(Location.class); + PlayerPortalEvent e = new PlayerPortalEvent(p, from, null); + Island island = mock(Island.class); + when(island.onIsland(any())).thenReturn(false); + PortalTeleportationListener np = new PortalTeleportationListener(plugin); + np.setSeachRadius(new PlayerEntityPortalEvent(e), island); + assertEquals(128, e.getSearchRadius()); + } }