diff --git a/src/main/java/world/bentobox/bentobox/Settings.java b/src/main/java/world/bentobox/bentobox/Settings.java index c149907b2..992aa0333 100644 --- a/src/main/java/world/bentobox/bentobox/Settings.java +++ b/src/main/java/world/bentobox/bentobox/Settings.java @@ -141,7 +141,6 @@ public class Settings implements ConfigObject { private Set fakePlayers = new HashSet<>(); /* PANELS */ - @ConfigComment("Toggle whether panels should be closed or not when the player clicks anywhere outside of the inventory view.") @ConfigEntry(path = "panel.close-on-click-outside") private boolean closePanelOnClickOutside = true; @@ -189,6 +188,13 @@ public class Settings implements ConfigObject { /* * Island */ + @ConfigComment("Override island distance mismatch checking. BentoBox normally refuses to run if") + @ConfigComment("the island distance in the gamemode config is different to the one stored in the database") + @ConfigComment("for safety. This overrides that check. You should never need this, and if you do not understand it") + @ConfigComment("keep it as false") + @ConfigEntry(path = "island.override-safety-check") + private boolean overrideSafetyCheck = false; + // Number of islands @ConfigComment("The default number of concurrent islands a player may have.") @ConfigComment("This may be overridden by individual game mode config settings.") @@ -1034,4 +1040,18 @@ public void setHideUsedBlueprints(boolean hideUsedBlueprints) { this.hideUsedBlueprints = hideUsedBlueprints; } + /** + * @return the overrideSafetyCheck + */ + public boolean isOverrideSafetyCheck() { + return overrideSafetyCheck; + } + + /** + * @param overrideSafetyCheck the overrideSafetyCheck to set + */ + public void setOverrideSafetyCheck(boolean overrideSafetyCheck) { + this.overrideSafetyCheck = overrideSafetyCheck; + } + } diff --git a/src/main/java/world/bentobox/bentobox/listeners/flags/worldsettings/CreeperListener.java b/src/main/java/world/bentobox/bentobox/listeners/flags/worldsettings/CreeperListener.java index 7adf0d6d6..6ea941017 100644 --- a/src/main/java/world/bentobox/bentobox/listeners/flags/worldsettings/CreeperListener.java +++ b/src/main/java/world/bentobox/bentobox/listeners/flags/worldsettings/CreeperListener.java @@ -8,8 +8,6 @@ import org.bukkit.entity.Player; import org.bukkit.event.EventHandler; import org.bukkit.event.EventPriority; -import org.bukkit.event.entity.EntityDamageByEntityEvent; -import org.bukkit.event.entity.EntityDamageEvent; import org.bukkit.event.entity.EntityExplodeEvent; import org.bukkit.event.player.PlayerInteractEntityEvent; @@ -40,9 +38,10 @@ public void onExplosion(final EntityExplodeEvent e) { if (!Flags.CREEPER_DAMAGE.isSetForWorld(e.getLocation().getWorld())) { // If any were removed, then prevent damage too e.blockList().clear(); - e.setCancelled(true); - return; + // Still allow player and mob damage + e.setCancelled(false); } + // Check for griefing Creeper creeper = (Creeper)e.getEntity(); if (!Flags.CREEPER_GRIEFING.isSetForWorld(e.getLocation().getWorld()) @@ -55,25 +54,6 @@ public void onExplosion(final EntityExplodeEvent e) { } } - - /** - * Prevent entities being damaged by explosion - * @param e - event - * @since 1.10.0 - */ - @EventHandler(priority = EventPriority.LOW, ignoreCancelled = true) - public void onExplosion(final EntityDamageByEntityEvent e) { - if (!e.getCause().equals(EntityDamageEvent.DamageCause.ENTITY_EXPLOSION) || !getIWM().inWorld(e.getEntity().getLocation()) - || !e.getDamager().getType().equals(EntityType.CREEPER)) { - return; - } - // If creeper damage is not allowed in world cancel the damage - if (!Flags.CREEPER_DAMAGE.isSetForWorld(e.getEntity().getWorld())) { - e.setCancelled(true); - } - } - - /** * Prevent creepers from igniting if they are not allowed to grief * @param e - event diff --git a/src/main/java/world/bentobox/bentobox/managers/IslandsManager.java b/src/main/java/world/bentobox/bentobox/managers/IslandsManager.java index 7e594c111..e532d6a03 100644 --- a/src/main/java/world/bentobox/bentobox/managers/IslandsManager.java +++ b/src/main/java/world/bentobox/bentobox/managers/IslandsManager.java @@ -1267,7 +1267,8 @@ public void load() throws IOException { // These will be deleted later deletedIslands.add(island.getUniqueId()); } // Check island distance and if incorrect stop BentoBox - else if (island.getWorld() != null && plugin.getIWM().inWorld(island.getWorld()) + else if (!plugin.getSettings().isOverrideSafetyCheck() && island.getWorld() != null + && plugin.getIWM().inWorld(island.getWorld()) && island.getRange() != plugin.getIWM().getIslandDistance(island.getWorld())) { throw new IOException("Island distance mismatch!\n" + "World '" + island.getWorld().getName() + "' distance " + plugin.getIWM().getIslandDistance(island.getWorld()) + " != island range " diff --git a/src/test/java/world/bentobox/bentobox/listeners/flags/worldsettings/CreeperListenerTest.java b/src/test/java/world/bentobox/bentobox/listeners/flags/worldsettings/CreeperListenerTest.java new file mode 100644 index 000000000..2733fe692 --- /dev/null +++ b/src/test/java/world/bentobox/bentobox/listeners/flags/worldsettings/CreeperListenerTest.java @@ -0,0 +1,136 @@ +package world.bentobox.bentobox.listeners.flags.worldsettings; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import java.util.ArrayList; +import java.util.List; + +import org.bukkit.Bukkit; +import org.bukkit.block.Block; +import org.bukkit.entity.Creeper; +import org.bukkit.entity.Entity; +import org.bukkit.entity.EntityType; +import org.bukkit.event.entity.EntityExplodeEvent; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mockito; +import org.powermock.core.classloader.annotations.PrepareForTest; +import org.powermock.modules.junit4.PowerMockRunner; + +import world.bentobox.bentobox.BentoBox; +import world.bentobox.bentobox.api.user.User; +import world.bentobox.bentobox.listeners.flags.AbstractCommonSetup; +import world.bentobox.bentobox.lists.Flags; +import world.bentobox.bentobox.util.Util; + +/** + * + */ +@RunWith(PowerMockRunner.class) +@PrepareForTest({ Bukkit.class, BentoBox.class, Flags.class, Util.class }) +public class CreeperListenerTest extends AbstractCommonSetup { + + private CreeperListener cl; + + /** + * @throws java.lang.Exception + */ + @Before + public void setUp() throws Exception { + super.setUp(); + + cl = new CreeperListener(); + } + + /** + * @throws java.lang.Exception + */ + @After + public void tearDown() throws Exception { + User.clearUsers(); + Mockito.framework().clearInlineMocks(); + } + + /** + * Test method for {@link world.bentobox.bentobox.listeners.flags.worldsettings.CreeperListener#onExplosion(org.bukkit.event.entity.EntityExplodeEvent)}. + */ + @Test + public void testOnExplosionNotCreeper() { + List list = new ArrayList<>(); + Entity entity = mock(Entity.class); + when(entity.getType()).thenReturn(EntityType.TNT); + when(iwm.inWorld(location)).thenReturn(true); + EntityExplodeEvent event = new EntityExplodeEvent(entity, location, list, 0); + cl.onExplosion(event); + assertFalse(event.isCancelled()); + } + + /** + * Test method for {@link world.bentobox.bentobox.listeners.flags.worldsettings.CreeperListener#onExplosion(org.bukkit.event.entity.EntityExplodeEvent)}. + */ + @Test + public void testOnExplosionNotInWorld() { + List list = new ArrayList<>(); + Entity entity = mock(Entity.class); + when(entity.getLocation()).thenReturn(location); + when(entity.getType()).thenReturn(EntityType.CREEPER); + when(iwm.inWorld(location)).thenReturn(false); + EntityExplodeEvent event = new EntityExplodeEvent(entity, location, list, 0); + cl.onExplosion(event); + assertFalse(event.isCancelled()); + } + + /** + * Test method for {@link world.bentobox.bentobox.listeners.flags.worldsettings.CreeperListener#onExplosion(org.bukkit.event.entity.EntityExplodeEvent)}. + */ + @Test + public void testOnExplosionCreeperInWorldDamageOK() { + List list = new ArrayList<>(); + list.add(mock(Block.class)); + list.add(mock(Block.class)); + list.add(mock(Block.class)); + Creeper entity = mock(Creeper.class); + when(entity.getLocation()).thenReturn(location); + when(entity.getType()).thenReturn(EntityType.CREEPER); + when(iwm.inWorld(location)).thenReturn(true); + EntityExplodeEvent event = new EntityExplodeEvent(entity, location, list, 0); + cl.onExplosion(event); + assertFalse(event.isCancelled()); + assertFalse(event.blockList().isEmpty()); // No clearing of block list + } + + /** + * Test method for {@link world.bentobox.bentobox.listeners.flags.worldsettings.CreeperListener#onExplosion(org.bukkit.event.entity.EntityExplodeEvent)}. + */ + @Test + public void testOnExplosionCreeperInWorldDamageNOK() { + Flags.CREEPER_DAMAGE.setSetting(world, false); + List list = new ArrayList<>(); + list.add(mock(Block.class)); + list.add(mock(Block.class)); + list.add(mock(Block.class)); + Creeper entity = mock(Creeper.class); + when(location.getWorld()).thenReturn(world); + when(entity.getLocation()).thenReturn(location); + when(entity.getType()).thenReturn(EntityType.CREEPER); + when(iwm.inWorld(location)).thenReturn(true); + EntityExplodeEvent event = new EntityExplodeEvent(entity, location, list, 0); + cl.onExplosion(event); + assertFalse(event.isCancelled()); + assertTrue(event.blockList().isEmpty()); // No clearing of block list + } + + /** + * Test method for {@link world.bentobox.bentobox.listeners.flags.worldsettings.CreeperListener#onPlayerInteractEntity(org.bukkit.event.player.PlayerInteractEntityEvent)}. + */ + @Test + public void testOnPlayerInteractEntity() { + //TODO + } + +} diff --git a/src/test/java/world/bentobox/bentobox/managers/IslandsManagerTest.java b/src/test/java/world/bentobox/bentobox/managers/IslandsManagerTest.java index fa84ccec0..fc2967a0d 100644 --- a/src/test/java/world/bentobox/bentobox/managers/IslandsManagerTest.java +++ b/src/test/java/world/bentobox/bentobox/managers/IslandsManagerTest.java @@ -153,6 +153,8 @@ public class IslandsManagerTest extends AbstractCommonSetup { // Class under test IslandsManager im; + private Settings settings; + @SuppressWarnings("unchecked") @BeforeClass public static void beforeClass() throws IllegalAccessException, InvocationTargetException, IntrospectionException { @@ -187,12 +189,12 @@ public void setUp() throws Exception { when(world.getEnvironment()).thenReturn(World.Environment.NORMAL); when(iwm.inWorld(any(World.class))).thenReturn(true); when(iwm.inWorld(any(Location.class))).thenReturn(true); + when(iwm.getIslandDistance(any())).thenReturn(25); when(plugin.getIWM()).thenReturn(iwm); // Settings - Settings s = mock(Settings.class); - when(plugin.getSettings()).thenReturn(s); - when(s.getDatabaseType()).thenReturn(DatabaseType.JSON); + settings = new Settings(); + when(plugin.getSettings()).thenReturn(settings); // World when(world.getEnvironment()).thenReturn(World.Environment.NORMAL); @@ -827,12 +829,39 @@ public void testIsAtSpawn() { /** * Test method for * {@link world.bentobox.bentobox.managers.IslandsManager#load()}. + * @throws IOException + * @throws IntrospectionException + * @throws NoSuchMethodException + * @throws ClassNotFoundException + * @throws InvocationTargetException + * @throws IllegalAccessException + * @throws InstantiationException */ @Test - public void testLoad() { - // - // im.load(); + public void testLoad() throws IOException, InstantiationException, IllegalAccessException, + InvocationTargetException, ClassNotFoundException, NoSuchMethodException, IntrospectionException { + when(island.getRange()).thenReturn(100); + when(h.loadObjects()).thenReturn(List.of(island)); + try { + im.load(); + } catch (IOException e) { + assertEquals("Island distance mismatch!\n" + "World 'world' distance 25 != island range 100!\n" + + "Island ID in database is null.\n" + + "Island distance in config.yml cannot be changed mid-game! Fix config.yml or clean database.", + e.getMessage()); + } + + } + @Test + public void testLoadNoDistanceCheck() throws IOException, InstantiationException, IllegalAccessException, + InvocationTargetException, ClassNotFoundException, NoSuchMethodException, IntrospectionException { + settings.setOverrideSafetyCheck(true); + when(island.getUniqueId()).thenReturn(UUID.randomUUID().toString()); + when(island.getRange()).thenReturn(100); + when(h.loadObjects()).thenReturn(List.of(island)); + im.load(); + // No exception should be thrown } /**