Skip to content

Commit

Permalink
Merge pull request #2508 from BentoBoxWorld/2507_Allow_creepers_to_hu…
Browse files Browse the repository at this point in the history
…rt_but_not_destroy_blocks

Change how creeper damage flag works. #2507
  • Loading branch information
tastybento authored Sep 17, 2024
2 parents 060e700 + 4876064 commit e90755d
Show file tree
Hide file tree
Showing 5 changed files with 197 additions and 31 deletions.
22 changes: 21 additions & 1 deletion src/main/java/world/bentobox/bentobox/Settings.java
Original file line number Diff line number Diff line change
Expand Up @@ -141,7 +141,6 @@ public class Settings implements ConfigObject {
private Set<String> 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;
Expand Down Expand Up @@ -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.")
Expand Down Expand Up @@ -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;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -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())
Expand All @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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 "
Expand Down
Original file line number Diff line number Diff line change
@@ -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<Block> 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<Block> 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<Block> 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<Block> 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
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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
}

/**
Expand Down

0 comments on commit e90755d

Please sign in to comment.