diff --git a/src/main/java/world/bentobox/bentobox/listeners/flags/settings/MobTeleportListener.java b/src/main/java/world/bentobox/bentobox/listeners/flags/settings/MobTeleportListener.java new file mode 100644 index 000000000..db79e252d --- /dev/null +++ b/src/main/java/world/bentobox/bentobox/listeners/flags/settings/MobTeleportListener.java @@ -0,0 +1,39 @@ +package world.bentobox.bentobox.listeners.flags.settings; + +import java.util.Optional; + +import org.bukkit.World; +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.entity.EntityTeleportEvent; + +import world.bentobox.bentobox.api.flags.FlagListener; +import world.bentobox.bentobox.database.objects.Island; +import world.bentobox.bentobox.lists.Flags; + +/** + * Handles natural mob teleporting. + * @author tastybento + */ +public class MobTeleportListener extends FlagListener { + + /** + * Check teleport of Endermen + * @param event EntityTeleportEvent + */ + @EventHandler(priority = EventPriority.NORMAL, ignoreCancelled = true) + public void onEntityTeleportEvent(EntityTeleportEvent event) { + World w = event.getFrom().getWorld(); + // If not in the right world exit immediately. + if (!this.getIWM().inWorld(w)) { + return; + } + Optional island = getIslands().getIslandAt(event.getEntity().getLocation()); + + if (Boolean.TRUE.equals(island.map(i -> !i.isAllowed(Flags.ENDERMAN_TELEPORT)).orElseGet( + () -> !Flags.ENDERMAN_TELEPORT.isSetForWorld(w)))) { + // Enderman teleport is disabled on island or world. Cancel it. + event.setCancelled(true); + } + } +} \ No newline at end of file diff --git a/src/main/java/world/bentobox/bentobox/lists/Flags.java b/src/main/java/world/bentobox/bentobox/lists/Flags.java index 10ad9570e..073fb83e5 100644 --- a/src/main/java/world/bentobox/bentobox/lists/Flags.java +++ b/src/main/java/world/bentobox/bentobox/lists/Flags.java @@ -42,6 +42,7 @@ import world.bentobox.bentobox.listeners.flags.protection.TeleportationListener; import world.bentobox.bentobox.listeners.flags.protection.ThrowingListener; import world.bentobox.bentobox.listeners.flags.settings.DecayListener; import world.bentobox.bentobox.listeners.flags.settings.MobSpawnListener; +import world.bentobox.bentobox.listeners.flags.settings.MobTeleportListener; import world.bentobox.bentobox.listeners.flags.settings.PVPListener; import world.bentobox.bentobox.listeners.flags.worldsettings.ChestDamageListener; import world.bentobox.bentobox.listeners.flags.worldsettings.CleanSuperFlatListener; @@ -488,6 +489,13 @@ public final class Flags { .listener(new EndermanListener()) .build(); + /** + * If {@code false}, prevents Endermans from teleporting + * @since 1.22.1 + */ + public static final Flag ENDERMAN_TELEPORT = new Flag.Builder("ENDERMAN_TELEPORT", Material.ENDER_PEARL).type(Type.SETTING) + .defaultSetting(true).listener(new MobTeleportListener()).mode(Flag.Mode.ADVANCED).build(); + public static final Flag ENTER_EXIT_MESSAGES = new Flag.Builder("ENTER_EXIT_MESSAGES", Material.DIRT).defaultSetting(true).type(Type.WORLD_SETTING) .listener(new EnterExitListener()) .build(); diff --git a/src/main/resources/locales/en-US.yml b/src/main/resources/locales/en-US.yml index ecf47e868..afdeb2cd4 100644 --- a/src/main/resources/locales/en-US.yml +++ b/src/main/resources/locales/en-US.yml @@ -971,6 +971,11 @@ protection: &a Endermen can remove &a blocks from islands name: "Enderman griefing" + ENDERMAN_TELEPORT: + description: |- + &a Endermen can teleport + &a if active. + name: "Enderman teleport" ENDER_PEARL: description: "Toggle use" name: "EnderPearls" diff --git a/src/test/java/world/bentobox/bentobox/listeners/flags/settings/MobTeleportListenerTest.java b/src/test/java/world/bentobox/bentobox/listeners/flags/settings/MobTeleportListenerTest.java new file mode 100644 index 000000000..aa6523a08 --- /dev/null +++ b/src/test/java/world/bentobox/bentobox/listeners/flags/settings/MobTeleportListenerTest.java @@ -0,0 +1,148 @@ +package world.bentobox.bentobox.listeners.flags.settings; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import java.util.Optional; + +import org.bukkit.Location; +import org.bukkit.World; +import org.bukkit.entity.Entity; +import org.bukkit.entity.EntityType; +import org.bukkit.event.entity.EntityTeleportEvent; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.Mockito; +import org.powermock.api.mockito.PowerMockito; +import org.powermock.core.classloader.annotations.PrepareForTest; +import org.powermock.modules.junit4.PowerMockRunner; + +import world.bentobox.bentobox.BentoBox; +import world.bentobox.bentobox.database.objects.Island; +import world.bentobox.bentobox.lists.Flags; +import world.bentobox.bentobox.managers.IslandWorldManager; +import world.bentobox.bentobox.managers.IslandsManager; + +/** + * @author tastybento + * + */ +@RunWith(PowerMockRunner.class) +@PrepareForTest({BentoBox.class }) +public class MobTeleportListenerTest { + + @Mock + private IslandWorldManager iwm; + @Mock + private IslandsManager im; + @Mock + private Island island; + private MobTeleportListener mtl; + + @Mock + private Entity enderman; + @Mock + private Entity other; + @Mock + private Location from; + @Mock + private Location to; + @Mock + private World world; + /** + * @throws java.lang.Exception + */ + @Before + public void setUp() throws Exception { + // Set up plugin + BentoBox plugin = mock(BentoBox.class); + PowerMockito.mockStatic(BentoBox.class, Mockito.RETURNS_MOCKS); + when(BentoBox.getInstance()).thenReturn(plugin); + // Island World Manager + when(plugin.getIWM()).thenReturn(iwm); + when(iwm.inWorld(any(World.class))).thenReturn(true); + // Island Manager + when(plugin.getIslands()).thenReturn(im); + when(im.getIslandAt(any())).thenReturn(Optional.of(island)); + when(island.isAllowed(Flags.ENDERMAN_TELEPORT)).thenReturn(true); + + when(to.getWorld()).thenReturn(world); + when(from.getWorld()).thenReturn(world); + when(enderman.getType()).thenReturn(EntityType.ENDERMAN); + when(other.getType()).thenReturn(EntityType.AXOLOTL); + mtl = new MobTeleportListener(); + + } + + /** + * @throws java.lang.Exception + */ + @After + public void tearDown() throws Exception { + Mockito.framework().clearInlineMocks(); + } + + /** + * Test method for {@link world.bentobox.bentobox.listeners.flags.settings.MobTeleportListener#onEntityTeleportEvent(org.bukkit.event.entity.EntityTeleportEvent)}. + */ + @Test + public void testOnEntityTeleportEventEndermanNotAllowed() { + Flags.ENDERMAN_TELEPORT.setSetting(world, false); + when(island.isAllowed(Flags.ENDERMAN_TELEPORT)).thenReturn(false); + EntityTeleportEvent e = new EntityTeleportEvent(enderman, from, to); + mtl.onEntityTeleportEvent(e); + assertTrue(e.isCancelled()); + } + + /** + * Test method for {@link world.bentobox.bentobox.listeners.flags.settings.MobTeleportListener#onEntityTeleportEvent(org.bukkit.event.entity.EntityTeleportEvent)}. + */ + @Test + public void testOnEntityTeleportEventEndermanNotAllowedWrongWorld() { + when(iwm.inWorld(any(World.class))).thenReturn(false); + Flags.ENDERMAN_TELEPORT.setSetting(world, false); + when(island.isAllowed(Flags.ENDERMAN_TELEPORT)).thenReturn(false); + EntityTeleportEvent e = new EntityTeleportEvent(enderman, from, to); + mtl.onEntityTeleportEvent(e); + assertFalse(e.isCancelled()); + } + + /** + * Test method for {@link world.bentobox.bentobox.listeners.flags.settings.MobTeleportListener#onEntityTeleportEvent(org.bukkit.event.entity.EntityTeleportEvent)}. + */ + @Test + public void testOnEntityTeleportEventEndermanNotAllowedNotOnIsland() { + when(im.getIslandAt(any())).thenReturn(Optional.empty()); + Flags.ENDERMAN_TELEPORT.setSetting(world, false); + EntityTeleportEvent e = new EntityTeleportEvent(enderman, from, to); + mtl.onEntityTeleportEvent(e); + assertFalse(e.isCancelled()); + } + + /** + * Test method for {@link world.bentobox.bentobox.listeners.flags.settings.MobTeleportListener#onEntityTeleportEvent(org.bukkit.event.entity.EntityTeleportEvent)}. + */ + @Test + public void testOnEntityTeleportEventEndermanAllowedDefault() { + EntityTeleportEvent e = new EntityTeleportEvent(enderman, from, to); + mtl.onEntityTeleportEvent(e); + assertFalse(e.isCancelled()); + } + + /** + * Test method for {@link world.bentobox.bentobox.listeners.flags.settings.MobTeleportListener#onEntityTeleportEvent(org.bukkit.event.entity.EntityTeleportEvent)}. + */ + @Test + public void testOnEntityTeleportEventOther() { + EntityTeleportEvent e = new EntityTeleportEvent(other, from, to); + mtl.onEntityTeleportEvent(e); + assertFalse(e.isCancelled()); + } + +}