diff --git a/src/main/java/world/bentobox/bentobox/BentoBox.java b/src/main/java/world/bentobox/bentobox/BentoBox.java index e781b1e2a..9da499111 100644 --- a/src/main/java/world/bentobox/bentobox/BentoBox.java +++ b/src/main/java/world/bentobox/bentobox/BentoBox.java @@ -26,6 +26,7 @@ import world.bentobox.bentobox.listeners.JoinLeaveListener; import world.bentobox.bentobox.listeners.NetherPortals; import world.bentobox.bentobox.listeners.PanelListenerManager; import world.bentobox.bentobox.listeners.NetherTreesListener; +import world.bentobox.bentobox.listeners.StandardSpawnProtectionListener; import world.bentobox.bentobox.managers.AddonsManager; import world.bentobox.bentobox.managers.CommandsManager; import world.bentobox.bentobox.managers.FlagsManager; @@ -204,6 +205,8 @@ public class BentoBox extends JavaPlugin { manager.registerEvents(new JoinLeaveListener(this), this); // Panel listener manager manager.registerEvents(new PanelListenerManager(), this); + // Standard Nether/End spawns protection + manager.registerEvents(new StandardSpawnProtectionListener(this), this); // Nether portals manager.registerEvents(new NetherPortals(this), this); // Nether trees conversion diff --git a/src/main/java/world/bentobox/bentobox/listeners/NetherPortals.java b/src/main/java/world/bentobox/bentobox/listeners/NetherPortals.java index 60a6905ba..4537af624 100644 --- a/src/main/java/world/bentobox/bentobox/listeners/NetherPortals.java +++ b/src/main/java/world/bentobox/bentobox/listeners/NetherPortals.java @@ -30,77 +30,13 @@ import world.bentobox.bentobox.util.Util; import world.bentobox.bentobox.util.teleport.SafeSpotTeleport; public class NetherPortals implements Listener { - private static final String SPAWN_PROTECTED = "protection.spawn-protected"; + private final BentoBox plugin; public NetherPortals(@NonNull BentoBox plugin) { this.plugin = plugin; } - /** - * Function to check proximity to nether or end spawn location. - * Used when playing with the standard nether or end. - * - * @param location - the location - * @return true if in the spawn area, false if not - */ - private boolean atSpawn(Location location) { - Vector p = location.toVector().multiply(new Vector(1, 0, 1)); - Vector spawn = location.getWorld().getSpawnLocation().toVector().multiply(new Vector(1, 0, 1)); - int radiusSquared = plugin.getIWM().getNetherSpawnRadius(location.getWorld()) * plugin.getIWM().getNetherSpawnRadius(location.getWorld()); - return (spawn.distanceSquared(p) < radiusSquared); - } - - /** - * If the player is not in the standard nether or standard end or op, do nothing. - * Used to protect the standard spawn for nether or end - * @param player - the player - * @return true if nothing needs to be done - */ - private boolean noAction(Player player) { - if (player.isOp() - || player.getWorld().getEnvironment().equals(Environment.NORMAL) - || !plugin.getIWM().inWorld(player.getLocation())) { - return true; - } - // Player is in an island world and in a nether or end - return (player.getWorld().getEnvironment().equals(Environment.NETHER) && plugin.getIWM().isNetherIslands(player.getWorld())) - || (player.getWorld().getEnvironment().equals(Environment.THE_END) && plugin.getIWM().isEndIslands(player.getWorld())); - } - - /** - * Prevents blocks from being broken - * - * @param e - event - */ - @EventHandler(priority = EventPriority.LOW, ignoreCancelled = true) - public void onBlockBreak(BlockBreakEvent e) { - if (noAction(e.getPlayer())) { - return; - } - if (atSpawn(e.getBlock().getLocation())) { - User user = User.getInstance(e.getPlayer()); - user.sendMessage(SPAWN_PROTECTED, TextVariables.DESCRIPTION, user.getTranslation(Flags.BREAK_BLOCKS.getHintReference())); - e.setCancelled(true); - } - } - - /** - * Protects standard nether or end spawn from bucket abuse - * @param e - event - */ - @EventHandler(priority = EventPriority.LOW, ignoreCancelled = true) - public void onBucketEmpty(PlayerBucketEmptyEvent e) { - if (noAction(e.getPlayer())) { - return; - } - if (atSpawn(e.getBlockClicked().getLocation())) { - User user = User.getInstance(e.getPlayer()); - user.sendMessage(SPAWN_PROTECTED, TextVariables.DESCRIPTION, user.getTranslation(Flags.BUCKET.getHintReference())); - e.setCancelled(true); - } - } - /** * Handle end portals * @param e - event @@ -148,28 +84,6 @@ public class NetherPortals implements Listener { } } - /** - * Prevent standard nether or end spawns from being blown up - * - * @param e - event - */ - @EventHandler(priority = EventPriority.LOW, ignoreCancelled = true) - public boolean onExplosion(EntityExplodeEvent e) { - if (!plugin.getIWM().inWorld(e.getLocation()) - || plugin.getIWM().isIslandNether(e.getLocation().getWorld()) - || plugin.getIWM().isIslandEnd(e.getLocation().getWorld())) { - // Not used in island worlds - return false; - } - // Find out what is exploding - Entity expl = e.getEntity(); - if (expl == null) { - return false; - } - e.blockList().removeIf(b -> atSpawn(b.getLocation())); - return true; - } - /** * When returning from the standard nether, teleport to the player's island * @param e @@ -241,21 +155,4 @@ public class NetherPortals implements Listener { .build(); return true; } - - /** - * Prevents placing of blocks at standard nether or end spawns - * - * @param e - event - */ - @EventHandler(priority = EventPriority.LOW, ignoreCancelled = true) - public void onPlayerBlockPlace(BlockPlaceEvent e) { - if (noAction(e.getPlayer())) { - return; - } - if (atSpawn(e.getBlock().getLocation())) { - User user = User.getInstance(e.getPlayer()); - user.sendMessage(SPAWN_PROTECTED, TextVariables.DESCRIPTION, user.getTranslation(Flags.PLACE_BLOCKS.getHintReference())); - e.setCancelled(true); - } - } } \ No newline at end of file diff --git a/src/main/java/world/bentobox/bentobox/listeners/StandardSpawnProtectionListener.java b/src/main/java/world/bentobox/bentobox/listeners/StandardSpawnProtectionListener.java new file mode 100644 index 000000000..6610f0a1e --- /dev/null +++ b/src/main/java/world/bentobox/bentobox/listeners/StandardSpawnProtectionListener.java @@ -0,0 +1,139 @@ +package world.bentobox.bentobox.listeners; + +import org.bukkit.Location; +import org.bukkit.World; +import org.bukkit.entity.Entity; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.Listener; +import org.bukkit.event.block.BlockBreakEvent; +import org.bukkit.event.block.BlockPlaceEvent; +import org.bukkit.event.entity.EntityExplodeEvent; +import org.bukkit.event.player.PlayerBucketEmptyEvent; +import org.bukkit.util.Vector; +import org.eclipse.jdt.annotation.NonNull; +import world.bentobox.bentobox.BentoBox; +import world.bentobox.bentobox.api.localization.TextVariables; +import world.bentobox.bentobox.api.user.User; +import world.bentobox.bentobox.lists.Flags; + +/** + * Handles protection of the standard Nether and/or End spawns. + * + * @author tastybento + */ +public class StandardSpawnProtectionListener implements Listener { + + private static final String SPAWN_PROTECTED = "protection.spawn-protected"; + + private final BentoBox plugin; + + public StandardSpawnProtectionListener(@NonNull BentoBox plugin) { + this.plugin = plugin; + } + + /** + * Prevents placing blocks at standard nether or end spawns. + * + * @param e - event + */ + @EventHandler(priority = EventPriority.LOW, ignoreCancelled = true) + public void onBlockPlace(BlockPlaceEvent e) { + if (noAction(e.getPlayer())) { + return; + } + if (atSpawn(e.getBlock().getLocation())) { + User user = User.getInstance(e.getPlayer()); + user.sendMessage(SPAWN_PROTECTED, TextVariables.DESCRIPTION, user.getTranslation(Flags.PLACE_BLOCKS.getHintReference())); + e.setCancelled(true); + } + } + + /** + * Prevents blocks from being broken. + * + * @param e - event + */ + @EventHandler(priority = EventPriority.LOW, ignoreCancelled = true) + public void onBlockBreak(BlockBreakEvent e) { + if (noAction(e.getPlayer())) { + return; + } + if (atSpawn(e.getBlock().getLocation())) { + User user = User.getInstance(e.getPlayer()); + user.sendMessage(SPAWN_PROTECTED, TextVariables.DESCRIPTION, user.getTranslation(Flags.BREAK_BLOCKS.getHintReference())); + e.setCancelled(true); + } + } + + /** + * Prevent standard nether or end spawns from being blown up. + * + * @param e - event + */ + @EventHandler(priority = EventPriority.LOW, ignoreCancelled = true) + public boolean onExplosion(EntityExplodeEvent e) { + if (!plugin.getIWM().inWorld(e.getLocation()) + || plugin.getIWM().isIslandNether(e.getLocation().getWorld()) + || plugin.getIWM().isIslandEnd(e.getLocation().getWorld())) { + // Not used in island worlds + return false; + } + // Find out what is exploding + Entity expl = e.getEntity(); + if (expl == null) { + return false; + } + e.blockList().removeIf(b -> atSpawn(b.getLocation())); + return true; + } + + /** + * Protects standard nether or end spawn from bucket abuse. + * @param e - event + */ + @EventHandler(priority = EventPriority.LOW, ignoreCancelled = true) + public void onBucketEmpty(PlayerBucketEmptyEvent e) { + if (noAction(e.getPlayer())) { + return; + } + if (atSpawn(e.getBlockClicked().getLocation())) { + User user = User.getInstance(e.getPlayer()); + user.sendMessage(SPAWN_PROTECTED, TextVariables.DESCRIPTION, user.getTranslation(Flags.BUCKET.getHintReference())); + e.setCancelled(true); + } + } + + /** + * Check proximity to nether or end spawn location. + * Used when playing with the standard nether or end. + * + * @param location - the location + * @return true if in the spawn area, false if not + */ + private boolean atSpawn(@NonNull Location location) { + Vector p = location.toVector().multiply(new Vector(1, 0, 1)); + Vector spawn = location.getWorld().getSpawnLocation().toVector().multiply(new Vector(1, 0, 1)); + int radiusSquared = plugin.getIWM().getNetherSpawnRadius(location.getWorld()) * plugin.getIWM().getNetherSpawnRadius(location.getWorld()); + return (spawn.distanceSquared(p) < radiusSquared); + } + + /** + * If the player is not in the standard nether or standard end or op, do nothing. + * Used to protect the standard spawn for nether or end. + * + * @param player - the player + * @return true if nothing needs to be done + */ + private boolean noAction(@NonNull Player player) { + if (player.isOp() + || player.getWorld().getEnvironment().equals(World.Environment.NORMAL) + || !plugin.getIWM().inWorld(player.getLocation())) { + return true; + } + // Player is in an island world and in a nether or end + return (player.getWorld().getEnvironment().equals(World.Environment.NETHER) && plugin.getIWM().isNetherIslands(player.getWorld())) + || (player.getWorld().getEnvironment().equals(World.Environment.THE_END) && plugin.getIWM().isEndIslands(player.getWorld())); + } +} diff --git a/src/test/java/world/bentobox/bentobox/listeners/NetherPortalsTest.java b/src/test/java/world/bentobox/bentobox/listeners/NetherPortalsTest.java index 93824e707..c7c8d5c20 100644 --- a/src/test/java/world/bentobox/bentobox/listeners/NetherPortalsTest.java +++ b/src/test/java/world/bentobox/bentobox/listeners/NetherPortalsTest.java @@ -153,107 +153,6 @@ public class NetherPortalsTest { when(iwm.inWorld(any(Location.class))).thenReturn(false); } - /** - * Test method for {@link world.bentobox.bentobox.listeners.NetherPortals#onBlockBreak(org.bukkit.event.block.BlockBreakEvent)}. - */ - @Test - public void testOnBlockBreakNoAction() { - NetherPortals np = new NetherPortals(plugin); - Block block = mock(Block.class); - Player player = mock(Player.class); - // Ops can do anything - when(player.isOp()).thenReturn(true); - BlockBreakEvent e = new BlockBreakEvent(block, player); - np.onBlockBreak(e); - assertFalse(e.isCancelled()); - // not op, but not in right world - when(player.isOp()).thenReturn(false); - World w = mock(World.class); - when(w.getEnvironment()).thenReturn(Environment.NORMAL); - when(player.getWorld()).thenReturn(w); - np.onBlockBreak(e); - assertFalse(e.isCancelled()); - // not op, island world - when(player.getWorld()).thenReturn(world); - np.onBlockBreak(e); - assertFalse(e.isCancelled()); - // Nether, but not standard nether - when(player.getWorld()).thenReturn(nether); - when(iwm.isNetherIslands(Mockito.any())).thenReturn(true); - np.onBlockBreak(e); - assertFalse(e.isCancelled()); - // End, but not standard end - when(player.getWorld()).thenReturn(nether); - when(iwm.isEndIslands(Mockito.any())).thenReturn(true); - np.onBlockBreak(e); - assertFalse(e.isCancelled()); - } - - /** - * Test method for {@link world.bentobox.bentobox.listeners.NetherPortals#onBlockBreak(org.bukkit.event.block.BlockBreakEvent)}. - */ - @Test - public void testOnBlockBreakActionAwayFromSpawn() { - NetherPortals np = new NetherPortals(plugin); - Block block = mock(Block.class); - Location far = mock(Location.class); - when(far.toVector()).thenReturn(new Vector(10000, 56, 2000)); - when(far.getWorld()).thenReturn(nether); - when(block.getLocation()).thenReturn(far); - Player player = mock(Player.class); - // Standard nether - when(player.getWorld()).thenReturn(nether); - when(iwm.isNetherIslands(world)).thenReturn(false); - - BlockBreakEvent e = new BlockBreakEvent(block, player); - np.onBlockBreak(e); - assertFalse(e.isCancelled()); - } - - /** - * Test method for {@link world.bentobox.bentobox.listeners.NetherPortals#onBlockBreak(org.bukkit.event.block.BlockBreakEvent)}. - */ - @Test - public void testOnBlockBreakActionAtSpawn() { - NetherPortals np = new NetherPortals(plugin); - Block block = mock(Block.class); - Location near = mock(Location.class); - when(near.toVector()).thenReturn(new Vector(0, 56, 0)); - when(near.getWorld()).thenReturn(nether); - when(block.getLocation()).thenReturn(near); - Player player = mock(Player.class); - // Standard nether - when(player.getWorld()).thenReturn(nether); - when(iwm.isNetherIslands(world)).thenReturn(false); - - BlockBreakEvent e = new BlockBreakEvent(block, player); - np.onBlockBreak(e); - Mockito.verify(block).getLocation(); - assertTrue(e.isCancelled()); - } - - /** - * Test method for {@link world.bentobox.bentobox.listeners.NetherPortals#onBucketEmpty(org.bukkit.event.player.PlayerBucketEmptyEvent)}. - */ - @Test - public void testOnBucketEmpty() { - NetherPortals np = new NetherPortals(plugin); - Block block = mock(Block.class); - Location near = mock(Location.class); - when(near.toVector()).thenReturn(new Vector(0, 56, 0)); - when(near.getWorld()).thenReturn(nether); - when(block.getLocation()).thenReturn(near); - Player player = mock(Player.class); - // Standard nether - when(player.getWorld()).thenReturn(nether); - when(iwm.isNetherIslands(world)).thenReturn(false); - - PlayerBucketEmptyEvent e = new PlayerBucketEmptyEvent(player, block, null, null, null); - np.onBucketEmpty(e); - Mockito.verify(block).getLocation(); - assertTrue(e.isCancelled()); - } - /** * Test method for {@link world.bentobox.bentobox.listeners.NetherPortals#onEndIslandPortal(org.bukkit.event.player.PlayerPortalEvent)}. */ @@ -365,170 +264,6 @@ public class NetherPortalsTest { assertTrue(e.isCancelled()); } - /** - * Test method for {@link world.bentobox.bentobox.listeners.NetherPortals#onExplosion(org.bukkit.event.entity.EntityExplodeEvent)}. - */ - @Test - public void testOnExplosionNotInWorld() { - NetherPortals np = new NetherPortals(plugin); - // Null entity - Entity en = null; - // Entity, Location, list of Blocks, yield - Block block = mock(Block.class); - Location blockLoc = mock(Location.class); - when(blockLoc.toVector()).thenReturn(new Vector(10000,0,10000)); - when(block.getLocation()).thenReturn(blockLoc); - when(blockLoc.getWorld()).thenReturn(nether); - // One block will be blown up by the wither - List affectedBlocks = new ArrayList<>(); - affectedBlocks.add(block); - - Location from = mock(Location.class); - when(from.getWorld()).thenReturn(mock(World.class)); - // Not in world - wrongWorld(); - - EntityExplodeEvent e = new EntityExplodeEvent(en, from, affectedBlocks, 0); - - assertFalse(np.onExplosion(e)); - } - - - @Test - public void testOnExplosionInWorldNotNetherOrEnd() { - NetherPortals np = new NetherPortals(plugin); - // Null entity - Entity en = null; - // Entity, Location, list of Blocks, yield - Block block = mock(Block.class); - Location blockLoc = mock(Location.class); - when(blockLoc.toVector()).thenReturn(new Vector(10000,0,10000)); - when(block.getLocation()).thenReturn(blockLoc); - when(blockLoc.getWorld()).thenReturn(nether); - // One block will be blown up by the wither - List affectedBlocks = new ArrayList<>(); - affectedBlocks.add(block); - - Location from = mock(Location.class); - when(from.getWorld()).thenReturn(mock(World.class)); - EntityExplodeEvent e = new EntityExplodeEvent(en, from, affectedBlocks, 0); - assertFalse(np.onExplosion(e)); - } - - @Test - public void testOnExplosionIslands() { - NetherPortals np = new NetherPortals(plugin); - // Null entity - Entity en = null; - // Entity, Location, list of Blocks, yield - Block block = mock(Block.class); - Location blockLoc = mock(Location.class); - when(blockLoc.toVector()).thenReturn(new Vector(10000,0,10000)); - when(block.getLocation()).thenReturn(blockLoc); - when(blockLoc.getWorld()).thenReturn(nether); - // One block will be blown up by the wither - List affectedBlocks = new ArrayList<>(); - affectedBlocks.add(block); - - Location from = mock(Location.class); - - // In world, in nether, nether islands - when(from.getWorld()).thenReturn(nether); - when(iwm.isNetherIslands(world)).thenReturn(true); - EntityExplodeEvent e = new EntityExplodeEvent(en, from, affectedBlocks, 0); - assertFalse(np.onExplosion(e)); - - // In world, in end, end islands - when(from.getWorld()).thenReturn(end); - when(iwm.isNetherIslands(world)).thenReturn(false); - when(iwm.isEndIslands(world)).thenReturn(true); - assertFalse(np.onExplosion(e)); - } - - @Test - public void testOnExplosionNullEntity() { - NetherPortals np = new NetherPortals(plugin); - // Entity, Location, list of Blocks, yield - Block block = mock(Block.class); - Location blockLoc = mock(Location.class); - when(blockLoc.toVector()).thenReturn(new Vector(10000,0,10000)); - when(block.getLocation()).thenReturn(blockLoc); - when(blockLoc.getWorld()).thenReturn(nether); - // One block will be blown up by the wither - List affectedBlocks = new ArrayList<>(); - affectedBlocks.add(block); - - Location from = mock(Location.class); - // In world, in nether, nether islands - when(from.getWorld()).thenReturn(nether); - when(iwm.isNetherIslands(world)).thenReturn(false); - EntityExplodeEvent e = new EntityExplodeEvent(null, from, affectedBlocks, 0); - assertFalse(np.onExplosion(e)); - } - - @Test - public void testOnExplosionAwayFromSpawn() { - NetherPortals np = new NetherPortals(plugin); - // Null entity - Entity en = mock(Entity.class); - // Entity, Location, list of Blocks, yield - Block block = mock(Block.class); - Location blockLoc = mock(Location.class); - when(blockLoc.toVector()).thenReturn(new Vector(10000,0,10000)); - when(block.getLocation()).thenReturn(blockLoc); - when(blockLoc.getWorld()).thenReturn(nether); - // One block will be blown up by the wither - List affectedBlocks = new ArrayList<>(); - affectedBlocks.add(block); - - Location from = mock(Location.class); - - // In world, in nether, standard nether, null entity - when(from.getWorld()).thenReturn(nether); - when(iwm.isNetherIslands(world)).thenReturn(false); - - EntityExplodeEvent e = new EntityExplodeEvent(en, from, affectedBlocks, 0); - // Real entity, away from spawn - assertTrue(np.onExplosion(e)); - // Block should still exist because it is away from spawn - assertFalse(e.blockList().isEmpty()); - } - - @Test - public void testOnExplosion() { - NetherPortals np = new NetherPortals(plugin); - // Null entity - Entity en = null; - // Entity, Location, list of Blocks, yield - Block block = mock(Block.class); - Location blockLoc = mock(Location.class); - when(blockLoc.toVector()).thenReturn(new Vector(10000,0,10000)); - when(block.getLocation()).thenReturn(blockLoc); - when(blockLoc.getWorld()).thenReturn(nether); - // One block will be blown up by the wither - List affectedBlocks = new ArrayList<>(); - affectedBlocks.add(block); - - Location from = mock(Location.class); - when(from.getWorld()).thenReturn(mock(World.class)); - // In world, in nether, standard nether, null entity - when(from.getWorld()).thenReturn(nether); - when(iwm.isNetherIslands(world)).thenReturn(false); - - - // Real entity, next to spawn - en = mock(Entity.class); - when(blockLoc.toVector()).thenReturn(new Vector(0,0,0)); - EntityExplodeEvent e = new EntityExplodeEvent(en, from, affectedBlocks, 0); - // Block exists before - assertFalse(e.blockList().isEmpty()); - assertTrue(np.onExplosion(e)); - // Block removed - assertTrue(e.blockList().isEmpty()); - - } - - /** * Test method for {@link world.bentobox.bentobox.listeners.NetherPortals#onNetherPortal(org.bukkit.event.player.PlayerPortalEvent)}. */ @@ -703,26 +438,4 @@ public class NetherPortalsTest { Mockito.verify(from).toVector(); Mockito.verify(im, Mockito.never()).getIslandLocation(Mockito.any(), Mockito.any()); } - - /** - * Test method for {@link world.bentobox.bentobox.listeners.NetherPortals#onPlayerBlockPlace(org.bukkit.event.block.BlockPlaceEvent)}. - */ - @Test - public void testOnPlayerBlockPlace() { - NetherPortals np = new NetherPortals(plugin); - Block block = mock(Block.class); - Location near = mock(Location.class); - when(near.toVector()).thenReturn(new Vector(0, 56, 0)); - when(near.getWorld()).thenReturn(nether); - when(block.getLocation()).thenReturn(near); - Player player = mock(Player.class); - // Standard nether - when(player.getWorld()).thenReturn(nether); - when(iwm.isNetherIslands(world)).thenReturn(false); - when(iwm.isNetherGenerate(world)).thenReturn(true); - BlockPlaceEvent e = new BlockPlaceEvent(block, null, block, null, player, false, null); - np.onPlayerBlockPlace(e); - Mockito.verify(block).getLocation(); - assertTrue(e.isCancelled()); - } }