Prevents named mobs from being cleared on teleport.

Adds a setting in BentoBox config to set the clear radius.
Adds defensive code to avoid clearing mobs in non game worlds.

https://github.com/BentoBoxWorld/BentoBox/issues/847
https://github.com/BentoBoxWorld/BentoBox/issues/819
This commit is contained in:
tastybento 2019-07-18 21:58:16 -07:00
parent 0f16d58ee7
commit e99f84f7c7
5 changed files with 129 additions and 55 deletions

View File

@ -149,6 +149,13 @@ public class Settings implements ConfigObject {
@ConfigEntry(path = "island.name.max-length")
private int nameMaxLength = 20;
@ConfigComment("Remove hostile mob on teleport box radius")
@ConfigComment("If hostile mobs are cleared on player teleport, then this sized box will be cleared")
@ConfigComment("around the player. e.g. 5 means a 10 x 10 x 10 box around the player")
@ConfigComment("Be careful not to make this too big. Does not cover standard nether or end teleports.")
@ConfigEntry(path = "island.clear-radius")
private int clearRadius = 5;
@ConfigComment("Number of blocks to paste per tick when pasting blueprints")
@ConfigComment("Smaller values will help reduce noticeable lag but will make pasting take longer")
@ConfigEntry(path = "island.paste-speed")
@ -491,4 +498,22 @@ public class Settings implements ConfigObject {
public void setDelayTime(int delayTime) {
this.delayTime = delayTime;
}
/**
* @return the clearRadius
*/
public int getClearRadius() {
if (clearRadius < 0) clearRadius = 0;
return clearRadius;
}
/**
* @param clearRadius the clearRadius to set. Cannot be negative.
*/
public void setClearRadius(int clearRadius) {
if (clearRadius < 0) clearRadius = 0;
this.clearRadius = clearRadius;
}
}

View File

@ -70,8 +70,8 @@ public class JoinLeaveListener implements Listener {
}
// If mobs have to be removed when a player joins, then wipe all the mobs on his island.
if (plugin.getIWM().inWorld(user.getLocation()) && Flags.REMOVE_MOBS.isSetForWorld(user.getWorld())) {
plugin.getIslands().clearArea(user.getLocation());
if (plugin.getIslands().locationIsOnIsland(event.getPlayer(), user.getLocation()) && Flags.REMOVE_MOBS.isSetForWorld(user.getWorld())) {
Bukkit.getScheduler().runTask(plugin, () -> plugin.getIslands().clearArea(user.getLocation()));
}
// Clear inventory if required

View File

@ -1031,14 +1031,20 @@ public class IslandsManager {
}
/**
* Clear an area of mobs as per world rules. Radius is 5 blocks in every direction.
* Clear an area of mobs as per world rules. Radius is default 5 blocks in every direction.
* Value is set in BentoBox config.yml
* Will not remove any named monsters.
* @param loc - location to clear
*/
public void clearArea(Location loc) {
loc.getWorld().getNearbyEntities(loc, 5D, 5D, 5D).stream()
if (!plugin.getIWM().inWorld(loc)) return;
loc.getWorld().getNearbyEntities(loc, plugin.getSettings().getClearRadius(),
plugin.getSettings().getClearRadius(),
plugin.getSettings().getClearRadius()).stream()
.filter(en -> Util.isHostileEntity(en)
&& !plugin.getIWM().getRemoveMobsWhitelist(loc.getWorld()).contains(en.getType())
&& !(en instanceof PufferFish))
.filter(en -> en.getCustomName() == null)
.forEach(Entity::remove);
}

View File

@ -85,6 +85,11 @@ island:
# These set the minimum and maximum size of a name.
min-length: 4
max-length: 20
# Remove hostile mob on teleport box radius
# If hostile mobs are cleared on player teleport, then this sized box will be cleared
# around the player. e.g. 5 means a 10 x 10 x 10 box around the player
# Be careful not to make this too big. Does not cover standard nether or end teleports.
clear-radius: 5
# Number of blocks to paste per tick when pasting blueprints
# Smaller values will help reduce noticeable lag but will make pasting take longer
paste-speed: 1000

View File

@ -6,9 +6,10 @@ import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.when;
import static org.mockito.Mockito.eq;
import java.io.File;
import java.io.IOException;
@ -43,6 +44,7 @@ import org.bukkit.entity.Entity;
import org.bukkit.entity.EntityType;
import org.bukkit.entity.Player;
import org.bukkit.entity.PufferFish;
import org.bukkit.entity.Skeleton;
import org.bukkit.entity.Slime;
import org.bukkit.entity.Wither;
import org.bukkit.entity.Zombie;
@ -110,6 +112,20 @@ public class IslandsManagerTest {
private PluginManager pim;
// Database
Database<Island> db;
@Mock
private Zombie zombie;
@Mock
private Slime slime;
@Mock
private Cow cow;
@Mock
private Wither wither;
@Mock
private Creeper creeper;
@Mock
private PufferFish pufferfish;
@Mock
private Skeleton skelly;
/**
* @throws java.lang.Exception
@ -221,6 +237,51 @@ public class IslandsManagerTest {
// Cover hostile entities
when(Util.isHostileEntity(Mockito.any())).thenCallRealMethod();
// Set up island entities
WorldSettings ws = mock(WorldSettings.class);
when(iwm.getWorldSettings(eq(world))).thenReturn(ws);
Map<String, Boolean> worldFlags = new HashMap<>();
when(ws.getWorldFlags()).thenReturn(worldFlags);
Flags.REMOVE_MOBS.setSetting(world, true);
// Default whitelist
Set<EntityType> whitelist = new HashSet<>();
whitelist.add(EntityType.ENDERMAN);
whitelist.add(EntityType.WITHER);
whitelist.add(EntityType.ZOMBIE_VILLAGER);
whitelist.add(EntityType.PIG_ZOMBIE);
when(iwm.getRemoveMobsWhitelist(Mockito.any())).thenReturn(whitelist);
// Monsters and animals
when(zombie.getLocation()).thenReturn(location);
when(zombie.getType()).thenReturn(EntityType.ZOMBIE);
when(slime.getLocation()).thenReturn(location);
when(slime.getType()).thenReturn(EntityType.SLIME);
when(cow.getLocation()).thenReturn(location);
when(cow.getType()).thenReturn(EntityType.COW);
when(wither.getType()).thenReturn(EntityType.WITHER);
when(creeper.getType()).thenReturn(EntityType.CREEPER);
when(pufferfish.getType()).thenReturn(EntityType.PUFFERFISH);
// Named monster
when(skelly.getType()).thenReturn(EntityType.SKELETON);
when(skelly.getCustomName()).thenReturn("Skelly");
Collection<Entity> collection = new ArrayList<>();
collection.add(player);
collection.add(zombie);
collection.add(cow);
collection.add(slime);
collection.add(wither);
collection.add(creeper);
collection.add(pufferfish);
collection.add(skelly);
when(world
.getNearbyEntities(Mockito.any(Location.class), Mockito.anyDouble(), Mockito.anyDouble(), Mockito.anyDouble()))
.thenReturn(collection);
// database must be mocked here
db = mock(Database.class);
}
@ -478,7 +539,7 @@ public class IslandsManagerTest {
*/
@Test
public void testDeleteIslandIslandBooleanRemoveBlocks() {
Mockito.verify(pim, Mockito.never()).callEvent(Mockito.any());
Mockito.verify(pim, never()).callEvent(Mockito.any());
IslandsManager im = new IslandsManager(plugin);
UUID owner = UUID.randomUUID();
Island island = im.createIsland(location, owner);
@ -976,61 +1037,38 @@ public class IslandsManagerTest {
* Test method for {@link world.bentobox.bentobox.managers.IslandsManager#clearArea(Location)}.
*/
@Test
public void testClearArea() {
WorldSettings ws = mock(WorldSettings.class);
when(iwm.getWorldSettings(eq(world))).thenReturn(ws);
Map<String, Boolean> worldFlags = new HashMap<>();
when(ws.getWorldFlags()).thenReturn(worldFlags);
Flags.REMOVE_MOBS.setSetting(world, true);
// Default whitelist
Set<EntityType> whitelist = new HashSet<>();
whitelist.add(EntityType.ENDERMAN);
whitelist.add(EntityType.WITHER);
whitelist.add(EntityType.ZOMBIE_VILLAGER);
whitelist.add(EntityType.PIG_ZOMBIE);
when(iwm.getRemoveMobsWhitelist(Mockito.any())).thenReturn(whitelist);
// Monsters and animals
Zombie zombie = mock(Zombie.class);
when(zombie.getLocation()).thenReturn(location);
when(zombie.getType()).thenReturn(EntityType.ZOMBIE);
Slime slime = mock(Slime.class);
when(slime.getLocation()).thenReturn(location);
when(slime.getType()).thenReturn(EntityType.SLIME);
Cow cow = mock(Cow.class);
when(cow.getLocation()).thenReturn(location);
when(cow.getType()).thenReturn(EntityType.COW);
Wither wither = mock(Wither.class);
when(wither.getType()).thenReturn(EntityType.WITHER);
Creeper creeper = mock(Creeper.class);
when(creeper.getType()).thenReturn(EntityType.CREEPER);
PufferFish pufferfish = mock(PufferFish.class);
when(pufferfish.getType()).thenReturn(EntityType.PUFFERFISH);
Collection<Entity> collection = new ArrayList<>();
collection.add(player);
collection.add(zombie);
collection.add(cow);
collection.add(slime);
collection.add(wither);
collection.add(creeper);
collection.add(pufferfish);
when(world
.getNearbyEntities(Mockito.any(Location.class), Mockito.anyDouble(), Mockito.anyDouble(), Mockito.anyDouble()))
.thenReturn(collection);
public void testClearAreaWrongWorld() {
when(iwm.inWorld(any(Location.class))).thenReturn(false);
IslandsManager im = new IslandsManager(plugin);
im.clearArea(location);
// No entities should be cleared
Mockito.verify(zombie, never()).remove();
Mockito.verify(player, never()).remove();
Mockito.verify(cow, never()).remove();
Mockito.verify(slime, never()).remove();
Mockito.verify(wither, never()).remove();
Mockito.verify(creeper, never()).remove();
Mockito.verify(pufferfish, never()).remove();
Mockito.verify(skelly, never()).remove();
}
/**
* Test method for {@link world.bentobox.bentobox.managers.IslandsManager#clearArea(Location)}.
*/
@Test
public void testClearArea() {
IslandsManager im = new IslandsManager(plugin);
im.clearArea(location);
// Only the correct entities should be cleared
Mockito.verify(zombie).remove();
Mockito.verify(player, Mockito.never()).remove();
Mockito.verify(cow, Mockito.never()).remove();
Mockito.verify(player, never()).remove();
Mockito.verify(cow, never()).remove();
Mockito.verify(slime).remove();
Mockito.verify(wither, Mockito.never()).remove();
Mockito.verify(wither, never()).remove();
Mockito.verify(creeper).remove();
Mockito.verify(pufferfish, Mockito.never()).remove();
Mockito.verify(pufferfish, never()).remove();
Mockito.verify(skelly, never()).remove();
}
/**