diff --git a/src/main/java/world/bentobox/bentobox/listeners/PortalTeleportationListener.java b/src/main/java/world/bentobox/bentobox/listeners/PortalTeleportationListener.java index 3f0402051..4be3a2564 100644 --- a/src/main/java/world/bentobox/bentobox/listeners/PortalTeleportationListener.java +++ b/src/main/java/world/bentobox/bentobox/listeners/PortalTeleportationListener.java @@ -20,8 +20,8 @@ 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; import org.bukkit.util.Vector; import org.eclipse.jdt.annotation.NonNull; @@ -44,17 +44,12 @@ public class PortalTeleportationListener implements Listener { private final BentoBox plugin; private Set inPortal; + private Set inTeleport; public PortalTeleportationListener(@NonNull BentoBox plugin) { this.plugin = plugin; inPortal = new HashSet<>(); - } - - - @EventHandler(priority = EventPriority.NORMAL, ignoreCancelled = true) - public void onPlayerTeleport(PlayerTeleportEvent e) { - // Remove player from inPortal after a teleport - inPortal.remove(e.getPlayer().getUniqueId()); + inTeleport = new HashSet<>(); } /** @@ -72,19 +67,13 @@ public class PortalTeleportationListener implements Listener { if (inPortal.contains(uuid) || !plugin.getIWM().inWorld(Util.getWorld(e.getLocation().getWorld()))) { return; } + inPortal.add(uuid); 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 (entity.getLocation().getBlock().getType().equals(Material.NETHER_PORTAL)) { - PlayerPortalEvent en = new PlayerPortalEvent((Player)entity, e.getLocation(), null, TeleportCause.NETHER_PORTAL, 0, false, 0); - if (!this.onIslandPortal(en)) { - // Failed - inPortal.remove(uuid); - } - } else { - inPortal.remove(uuid); + if (inPortal.contains(uuid)) { + this.onIslandPortal(new PlayerPortalEvent((Player)entity, e.getLocation(), null, TeleportCause.NETHER_PORTAL, 0, false, 0)); } }, 40); return; @@ -129,6 +118,21 @@ public class PortalTeleportationListener implements Listener { return false; } + /** + * Remove inPortal flag only when player exits the portal + * @param e player move event + */ + @EventHandler(priority = EventPriority.NORMAL, ignoreCancelled = true) + public void onExitPortal(PlayerMoveEvent e) { + if (!inPortal.contains(e.getPlayer().getUniqueId())) { + return; + } + if (e.getTo() != null && !e.getTo().getBlock().getType().equals(Material.NETHER_PORTAL)) { + inPortal.remove(e.getPlayer().getUniqueId()); + inTeleport.remove(e.getPlayer().getUniqueId()); + } + } + /** * Handles nether or end portals * @param e - event @@ -136,9 +140,9 @@ public class PortalTeleportationListener implements Listener { @EventHandler(priority = EventPriority.HIGH, ignoreCancelled = true) public boolean onIslandPortal(PlayerPortalEvent e) { return switch (e.getCause()) { - case END_GATEWAY, END_PORTAL -> processPortal(new PlayerEntityPortalEvent(e), Environment.THE_END); - case NETHER_PORTAL -> processPortal(new PlayerEntityPortalEvent(e), Environment.NETHER); - default -> false; + case END_GATEWAY, END_PORTAL -> processPortal(new PlayerEntityPortalEvent(e), Environment.THE_END); + case NETHER_PORTAL -> processPortal(new PlayerEntityPortalEvent(e), Environment.NETHER); + default -> false; }; } @@ -150,18 +154,27 @@ public class PortalTeleportationListener implements Listener { * @return true if portal happens, false if not */ 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)) { // Do nothing special return false; } - // 1.14.4 requires explicit cancellation to prevent teleporting to the normal nether + if (!isGenerate(overWorld, env)) { e.setCancelled(true); return false; } + + if (!Bukkit.getServer().getAllowNether()) { + e.setCancelled(true); + } + + if (inTeleport.contains(e.getEntity().getUniqueId())) { + return false; + } + inTeleport.add(e.getEntity().getUniqueId()); + // STANDARD NETHER OR END if (!isIslands(overWorld, env)) { handleStandardNetherOrEnd(e, fromWorld, overWorld, env); @@ -194,7 +207,6 @@ public class PortalTeleportationListener implements Listener { && e.getIsland().filter(i -> !hasPartnerIsland(i, env)).map(i -> { // No nether island present so paste the default one e.setCancelled(true); - inPortal.remove(e.getEntity().getUniqueId()); pasteNewIsland((Player)e.getEntity(), e.getTo(), i, env); return true; }).orElse(false)) { @@ -203,7 +215,6 @@ public class PortalTeleportationListener implements Listener { } if (e.getCanCreatePortal()) { // Let the server teleport - inPortal.remove(e.getEntity().getUniqueId()); return true; } if (env.equals(Environment.THE_END)) { @@ -228,7 +239,6 @@ public class PortalTeleportationListener implements Listener { .location(e.getTo()) .portal() .thenRun(() -> { - inPortal.remove(e.getEntity().getUniqueId()); e.getEntity().setVelocity(new Vector(0,0,0)); e.getEntity().setFallDistance(0); }) @@ -357,7 +367,6 @@ public class PortalTeleportationListener implements Listener { 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 @@ -370,7 +379,6 @@ public class PortalTeleportationListener implements Listener { .entity(e.getEntity()) .location(to) .portal() - .thenRun(() -> inPortal.remove(e.getEntity().getUniqueId())) .build(); } @@ -408,7 +416,7 @@ public class PortalTeleportationListener implements Listener { // 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())); + plugin.getIslands().homeTeleportAsync(overWorld, (Player)e.getEntity()); } } diff --git a/src/test/java/world/bentobox/bentobox/listeners/PortalTeleportationListenerTest.java b/src/test/java/world/bentobox/bentobox/listeners/PortalTeleportationListenerTest.java index b1f0dd5ce..d46c61259 100644 --- a/src/test/java/world/bentobox/bentobox/listeners/PortalTeleportationListenerTest.java +++ b/src/test/java/world/bentobox/bentobox/listeners/PortalTeleportationListenerTest.java @@ -7,7 +7,6 @@ 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; @@ -19,6 +18,7 @@ import java.util.concurrent.CompletableFuture; import org.bukkit.Bukkit; import org.bukkit.Location; import org.bukkit.Material; +import org.bukkit.Server; import org.bukkit.World; import org.bukkit.World.Environment; import org.bukkit.block.Block; @@ -154,6 +154,9 @@ public class PortalTeleportationListenerTest { BukkitScheduler sch = mock(BukkitScheduler.class); PowerMockito.mockStatic(Bukkit.class); when(Bukkit.getScheduler()).thenReturn(sch); + Server server = mock(Server.class); + when(server.getAllowNether()).thenReturn(true); + when(Bukkit.getServer()).thenReturn(server); // Locales LocalesManager lm = mock(LocalesManager.class); @@ -284,7 +287,7 @@ public class PortalTeleportationListenerTest { when(im.hasIsland(any(), any(UUID.class))).thenReturn(true); np.onIslandPortal(e); assertTrue(e.isCancelled()); - verify(im, times(2)).homeTeleportAsync(any(), eq(player)); + verify(im).homeTeleportAsync(any(), eq(player)); } /**