From eb855edfeb9e7380c83f69c7bb585f0e5af47bae Mon Sep 17 00:00:00 2001 From: tastybento Date: Sun, 24 Jun 2018 17:57:31 -0700 Subject: [PATCH] Adds white list for remove mobs to settings. --- config.yml | 33 ++++---- .../us/tastybento/bskyblock/Settings.java | 20 +++++ .../api/configuration/WorldSettings.java | 7 ++ .../listeners/JoinLeaveListener.java | 3 +- .../listeners/flags/RemoveMobsListener.java | 23 +----- .../managers/IslandWorldManager.java | 9 +++ .../bskyblock/managers/IslandsManager.java | 30 ++++++-- .../flags/RemoveMobsListenerTest.java | 73 +++--------------- .../managers/IslandsManagerTest.java | 77 ++++++++++++++++++- 9 files changed, 164 insertions(+), 111 deletions(-) diff --git a/config.yml b/config.yml index dbe3d6ba6..7891e35df 100644 --- a/config.yml +++ b/config.yml @@ -135,7 +135,6 @@ world: # a new island for example. Options are SURVIVAL, CREATIVE, ADVENTURE, SPECTATOR default-game-mode: SURVIVAL - ### Nether-related Settings ### nether: # Generate Nether - if this is false, the nether world will not be made and access to @@ -172,6 +171,21 @@ world: # Default is false, because it is an experimental feature that can break a lot of redstone systems. disable-offline-redstone: false + # Removing mobs - this kills all monsters in the vicinity. Benefit is that it helps + # players return to their island if the island has been overrun by monsters. + # This setting is toggled in world flags and set by the settings GUI + # Mob white list - these mobs will NOT be removed when logging in or doing /island + remove-mobs-whitelist: + - WITHER + - ENDERMAN + - PIG_ZOMBIE + - ZOMBIE_VILLAGER + + # World flags. These are boolean settings for various flags for this world + flags: + ENTER_EXIT_MESSAGES: true + REMOVE_MOBS: true + ### Island Settings ### island: # Default chest items @@ -269,23 +283,6 @@ island: # Reset Ender Chest - if true, the player's Ender Chest will be cleared. ender-chest: false - # Removing mobs - this kills all monsters in the vicinity. Benefit is that it helps - # players return to their island if the island has been overrun by monsters. - # Con is that it kills any mob grinders. - remove-mobs: - # Remove mobs on island when logging in. - on-login: false - - # Remove mobs when /island. - on-island: false - - # Mob white list - these mobs will NOT be removed when logging in or doing /island - whitelist: - - WITHER - - ENDERMAN - - PIG_ZOMBIE - #- ZOMBIE_VILLAGER (1.10+) - # Make island if player teleports to the island world and does not have one make-island-if-none: false diff --git a/src/main/java/us/tastybento/bskyblock/Settings.java b/src/main/java/us/tastybento/bskyblock/Settings.java index 32c89883c..8db97a69a 100644 --- a/src/main/java/us/tastybento/bskyblock/Settings.java +++ b/src/main/java/us/tastybento/bskyblock/Settings.java @@ -233,6 +233,13 @@ public class Settings implements DataObject, WorldSettings { @ConfigEntry(path = "world.disable-offline-redstone") private boolean disableOfflineRedstone = false; + @ConfigComment("Removing mobs - this kills all monsters in the vicinity. Benefit is that it helps") + @ConfigComment("players return to their island if the island has been overrun by monsters.") + @ConfigComment("This setting is toggled in world flags and set by the settings GUI.") + @ConfigComment("Mob white list - these mobs will NOT be removed when logging in or doing /island") + @ConfigEntry(path = "world.remove-mobs-whitelist") + private Set removeMobsWhitelist = new HashSet<>(); + @ConfigComment("World flags. These are boolean settings for various flags for this world") @ConfigEntry(path = "world.flags") private Map worldFlags = new HashMap<>(); @@ -1429,6 +1436,19 @@ public class Settings implements DataObject, WorldSettings { public void setDefaultGameMode(GameMode defaultGameMode) { this.defaultGameMode = defaultGameMode; } + /** + * @return the removeMobsWhitelist + */ + @Override + public Set getRemoveMobsWhitelist() { + return removeMobsWhitelist; + } + /** + * @param removeMobsWhitelist the removeMobsWhitelist to set + */ + public void setRemoveMobsWhitelist(Set removeMobsWhitelist) { + this.removeMobsWhitelist = removeMobsWhitelist; + } } \ No newline at end of file diff --git a/src/main/java/us/tastybento/bskyblock/api/configuration/WorldSettings.java b/src/main/java/us/tastybento/bskyblock/api/configuration/WorldSettings.java index 35f954e1b..65c29318c 100644 --- a/src/main/java/us/tastybento/bskyblock/api/configuration/WorldSettings.java +++ b/src/main/java/us/tastybento/bskyblock/api/configuration/WorldSettings.java @@ -2,6 +2,7 @@ package us.tastybento.bskyblock.api.configuration; import java.util.List; import java.util.Map; +import java.util.Set; import org.bukkit.GameMode; import org.bukkit.entity.EntityType; @@ -145,4 +146,10 @@ public interface WorldSettings { */ GameMode getDefaultGameMode(); + /** + * Get the set of entity types that should not be removed in this world when a player teleports to their island + * @return set of entity types + */ + Set getRemoveMobsWhitelist(); + } diff --git a/src/main/java/us/tastybento/bskyblock/listeners/JoinLeaveListener.java b/src/main/java/us/tastybento/bskyblock/listeners/JoinLeaveListener.java index 66d379dd9..5856454b7 100644 --- a/src/main/java/us/tastybento/bskyblock/listeners/JoinLeaveListener.java +++ b/src/main/java/us/tastybento/bskyblock/listeners/JoinLeaveListener.java @@ -10,7 +10,6 @@ import org.bukkit.event.player.PlayerQuitEvent; import us.tastybento.bskyblock.BSkyBlock; import us.tastybento.bskyblock.api.user.User; -import us.tastybento.bskyblock.listeners.flags.RemoveMobsListener; import us.tastybento.bskyblock.lists.Flags; import us.tastybento.bskyblock.managers.PlayersManager; @@ -53,7 +52,7 @@ public class JoinLeaveListener implements Listener { plugin.logWarning("Player that just logged in has no name! " + playerUUID.toString()); } if (plugin.getIWM().inWorld(user.getLocation()) && Flags.REMOVE_MOBS.isSetForWorld(user.getWorld())) { - RemoveMobsListener.clearArea(user.getLocation()); + plugin.getIslands().clearArea(user.getLocation()); } } } diff --git a/src/main/java/us/tastybento/bskyblock/listeners/flags/RemoveMobsListener.java b/src/main/java/us/tastybento/bskyblock/listeners/flags/RemoveMobsListener.java index ba92ba5ce..8e6f2109a 100644 --- a/src/main/java/us/tastybento/bskyblock/listeners/flags/RemoveMobsListener.java +++ b/src/main/java/us/tastybento/bskyblock/listeners/flags/RemoveMobsListener.java @@ -3,13 +3,6 @@ */ package us.tastybento.bskyblock.listeners.flags; -import java.util.HashSet; -import java.util.Set; - -import org.bukkit.Location; -import org.bukkit.entity.Entity; -import org.bukkit.entity.EntityType; -import org.bukkit.entity.Monster; import org.bukkit.event.EventHandler; import org.bukkit.event.EventPriority; import org.bukkit.event.player.PlayerTeleportEvent; @@ -27,21 +20,7 @@ public class RemoveMobsListener extends AbstractFlagListener { public void onUserTeleport(PlayerTeleportEvent e) { // Only process if flag is active if (getIslands().locationIsOnIsland(e.getPlayer(), e.getTo()) && Flags.REMOVE_MOBS.isSetForWorld(e.getTo().getWorld())) { - clearArea(e.getTo()); + getIslands().clearArea(e.getTo()); } } - - public static void clearArea(Location loc) { - Set keepers = new HashSet<>(); - keepers.add(EntityType.ZOMBIE_VILLAGER); - keepers.add(EntityType.PIG_ZOMBIE); - keepers.add(EntityType.WITHER); - keepers.add(EntityType.ENDERMAN); - keepers.add(EntityType.GHAST); - - loc.getWorld().getNearbyEntities(loc, 5D, 5D, 5D).stream() - .filter(en -> (en instanceof Monster)) - .filter(en -> !keepers.contains(en.getType())) - .forEach(Entity::remove); - } } diff --git a/src/main/java/us/tastybento/bskyblock/managers/IslandWorldManager.java b/src/main/java/us/tastybento/bskyblock/managers/IslandWorldManager.java index 7dacdde78..76380597d 100644 --- a/src/main/java/us/tastybento/bskyblock/managers/IslandWorldManager.java +++ b/src/main/java/us/tastybento/bskyblock/managers/IslandWorldManager.java @@ -486,4 +486,13 @@ public class IslandWorldManager { public GameMode getDefaultGameMode(World world) { return worldSettings.get(Util.getWorld(world)).getDefaultGameMode(); } + + /** + * Get the set of entity types not to remove when player teleports to island + * @param world - world + * @return - set of entity types + */ + public Set getRemoveMobsWhitelist(World world) { + return worldSettings.get(Util.getWorld(world)).getRemoveMobsWhitelist(); + } } diff --git a/src/main/java/us/tastybento/bskyblock/managers/IslandsManager.java b/src/main/java/us/tastybento/bskyblock/managers/IslandsManager.java index 4ebf83b6a..bf0b9c245 100644 --- a/src/main/java/us/tastybento/bskyblock/managers/IslandsManager.java +++ b/src/main/java/us/tastybento/bskyblock/managers/IslandsManager.java @@ -16,6 +16,7 @@ import org.bukkit.block.Block; import org.bukkit.block.BlockFace; import org.bukkit.entity.Boat; import org.bukkit.entity.Entity; +import org.bukkit.entity.Monster; import org.bukkit.entity.Player; import org.bukkit.inventory.ItemStack; import org.bukkit.material.MaterialData; @@ -119,6 +120,9 @@ public class IslandsManager { // Island Cache private IslandCache islandCache; + // Async database saving semaphore + private boolean midSave; + /** * Islands Manager * @param plugin - plugin @@ -693,9 +697,14 @@ public class IslandsManager { * @param async - if true, saving will be done async */ public void save(boolean async){ + if (midSave) { + // If it's already saving, then do nothing + return; + } Collection collection = islandCache.getIslands(); - if(async){ - Runnable save = () -> { + if(async) { + Bukkit.getScheduler().runTaskAsynchronously(plugin, () -> { + midSave = true; for(Island island : collection){ try { handler.saveObject(island); @@ -704,14 +713,14 @@ public class IslandsManager { e.printStackTrace(); } } - }; - Bukkit.getScheduler().runTaskAsynchronously(plugin, save); + midSave = false; + }); } else { for(Island island : collection){ try { handler.saveObject(island); } catch (Exception e) { - plugin.logError("Could not save island to datavase when running sync! " + e.getMessage()); + plugin.logError("Could not save island to database when running sync! " + e.getMessage()); } } } @@ -802,4 +811,15 @@ public class IslandsManager { } + /** + * Clear an area of mobs as per world rules. Radius is 5 blocks in every direction. + * @param loc - location to clear + */ + public void clearArea(Location loc) { + loc.getWorld().getNearbyEntities(loc, 5D, 5D, 5D).stream() + .filter(en -> (en instanceof Monster)) + .filter(en -> !plugin.getIWM().getRemoveMobsWhitelist(loc.getWorld()).contains(en.getType())) + .forEach(Entity::remove); + } + } diff --git a/src/test/java/us/tastybento/bskyblock/listeners/flags/RemoveMobsListenerTest.java b/src/test/java/us/tastybento/bskyblock/listeners/flags/RemoveMobsListenerTest.java index 5a979bc5e..64967d2c5 100644 --- a/src/test/java/us/tastybento/bskyblock/listeners/flags/RemoveMobsListenerTest.java +++ b/src/test/java/us/tastybento/bskyblock/listeners/flags/RemoveMobsListenerTest.java @@ -1,13 +1,11 @@ /** - * + * */ package us.tastybento.bskyblock.listeners.flags; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; -import java.util.ArrayList; -import java.util.Collection; import java.util.HashMap; import java.util.Map; import java.util.Optional; @@ -15,13 +13,7 @@ import java.util.UUID; import org.bukkit.Location; import org.bukkit.World; -import org.bukkit.entity.Cow; -import org.bukkit.entity.Entity; -import org.bukkit.entity.EntityType; import org.bukkit.entity.Player; -import org.bukkit.entity.Slime; -import org.bukkit.entity.Wither; -import org.bukkit.entity.Zombie; import org.bukkit.event.player.PlayerTeleportEvent; import org.junit.Before; import org.junit.Test; @@ -47,18 +39,13 @@ import us.tastybento.bskyblock.util.Util; @RunWith(PowerMockRunner.class) @PrepareForTest({BSkyBlock.class, Util.class }) public class RemoveMobsListenerTest { - + private Island island; private IslandsManager im; private World world; private Location inside; private UUID uuid; - private Zombie zombie; - private Slime slime; - private Cow cow; private Player player; - private Wither wither; - /** * @throws java.lang.Exception @@ -68,13 +55,13 @@ public class RemoveMobsListenerTest { // Set up plugin BSkyBlock plugin = mock(BSkyBlock.class); Whitebox.setInternalState(BSkyBlock.class, "instance", plugin); - + // World world = mock(World.class); - + // Owner uuid = UUID.randomUUID(); - + // Island initialization island = mock(Island.class); when(island.getOwner()).thenReturn(uuid); @@ -93,7 +80,7 @@ public class RemoveMobsListenerTest { PowerMockito.mockStatic(Util.class); when(Util.getWorld(Mockito.any())).thenReturn(world); - + // World Settings IslandWorldManager iwm = mock(IslandWorldManager.class); when(plugin.getIWM()).thenReturn(iwm); @@ -101,32 +88,8 @@ public class RemoveMobsListenerTest { when(iwm.getWorldSettings(Mockito.any())).thenReturn(ws); Map worldFlags = new HashMap<>(); when(ws.getWorldFlags()).thenReturn(worldFlags); - - // Monsters and animals - zombie = mock(Zombie.class); - when(zombie.getLocation()).thenReturn(inside); - when(zombie.getType()).thenReturn(EntityType.ZOMBIE); - slime = mock(Slime.class); - when(slime.getLocation()).thenReturn(inside); - when(slime.getType()).thenReturn(EntityType.SLIME); - cow = mock(Cow.class); - when(cow.getLocation()).thenReturn(inside); - when(cow.getType()).thenReturn(EntityType.COW); - wither = mock(Wither.class); - when(wither.getType()).thenReturn(EntityType.WITHER); - - - Collection collection = new ArrayList<>(); - collection.add(player); - collection.add(zombie); - collection.add(cow); - collection.add(slime); - collection.add(wither); - when(world - .getNearbyEntities(Mockito.any(Location.class), Mockito.anyDouble(), Mockito.anyDouble(), Mockito.anyDouble())) - .thenReturn(collection); Flags.REMOVE_MOBS.setSetting(world, true); - + // Sometimes use Mockito.withSettings().verboseLogging() player = mock(Player.class); UUID uuid = UUID.randomUUID(); @@ -141,13 +104,9 @@ public class RemoveMobsListenerTest { public void testOnUserTeleport() { PlayerTeleportEvent e = new PlayerTeleportEvent(player, inside, inside, PlayerTeleportEvent.TeleportCause.PLUGIN); new RemoveMobsListener().onUserTeleport(e); - Mockito.verify(zombie).remove(); - Mockito.verify(player, Mockito.never()).remove(); - Mockito.verify(cow, Mockito.never()).remove(); - Mockito.verify(slime, Mockito.never()).remove(); - Mockito.verify(wither, Mockito.never()).remove(); + Mockito.verify(im).clearArea(Mockito.any()); } - + /** * Test method for {@link us.tastybento.bskyblock.listeners.flags.RemoveMobsListener#onUserTeleport(org.bukkit.event.player.PlayerTeleportEvent)}. */ @@ -156,13 +115,9 @@ public class RemoveMobsListenerTest { Flags.REMOVE_MOBS.setSetting(world, false); PlayerTeleportEvent e = new PlayerTeleportEvent(player, inside, inside, PlayerTeleportEvent.TeleportCause.PLUGIN); new RemoveMobsListener().onUserTeleport(e); - Mockito.verify(zombie, Mockito.never()).remove(); - Mockito.verify(player, Mockito.never()).remove(); - Mockito.verify(cow, Mockito.never()).remove(); - Mockito.verify(slime, Mockito.never()).remove(); - Mockito.verify(wither, Mockito.never()).remove(); + Mockito.verify(im, Mockito.never()).clearArea(Mockito.any()); } - + /** * Test method for {@link us.tastybento.bskyblock.listeners.flags.RemoveMobsListener#onUserTeleport(org.bukkit.event.player.PlayerTeleportEvent)}. */ @@ -172,11 +127,7 @@ public class RemoveMobsListenerTest { when(im.locationIsOnIsland(Mockito.any(), Mockito.any())).thenReturn(false); PlayerTeleportEvent e = new PlayerTeleportEvent(player, inside, inside, PlayerTeleportEvent.TeleportCause.PLUGIN); new RemoveMobsListener().onUserTeleport(e); - Mockito.verify(zombie, Mockito.never()).remove(); - Mockito.verify(player, Mockito.never()).remove(); - Mockito.verify(cow, Mockito.never()).remove(); - Mockito.verify(slime, Mockito.never()).remove(); - Mockito.verify(wither, Mockito.never()).remove(); + Mockito.verify(im, Mockito.never()).clearArea(Mockito.any()); } } diff --git a/src/test/java/us/tastybento/bskyblock/managers/IslandsManagerTest.java b/src/test/java/us/tastybento/bskyblock/managers/IslandsManagerTest.java index 9c3f6168a..9944d464f 100644 --- a/src/test/java/us/tastybento/bskyblock/managers/IslandsManagerTest.java +++ b/src/test/java/us/tastybento/bskyblock/managers/IslandsManagerTest.java @@ -9,8 +9,12 @@ import static org.mockito.Matchers.any; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; +import java.util.ArrayList; import java.util.Arrays; +import java.util.Collection; +import java.util.HashMap; import java.util.HashSet; +import java.util.Map; import java.util.Optional; import java.util.Set; import java.util.UUID; @@ -23,7 +27,14 @@ import org.bukkit.World; import org.bukkit.block.Block; import org.bukkit.block.BlockFace; import org.bukkit.block.BlockState; +import org.bukkit.entity.Cow; +import org.bukkit.entity.Creeper; +import org.bukkit.entity.Entity; +import org.bukkit.entity.EntityType; import org.bukkit.entity.Player; +import org.bukkit.entity.Slime; +import org.bukkit.entity.Wither; +import org.bukkit.entity.Zombie; import org.bukkit.material.MaterialData; import org.bukkit.material.TrapDoor; import org.bukkit.scheduler.BukkitScheduler; @@ -43,8 +54,10 @@ import com.google.common.collect.ImmutableSet.Builder; import us.tastybento.bskyblock.BSkyBlock; import us.tastybento.bskyblock.Settings; +import us.tastybento.bskyblock.api.configuration.WorldSettings; import us.tastybento.bskyblock.api.user.User; import us.tastybento.bskyblock.database.objects.Island; +import us.tastybento.bskyblock.lists.Flags; import us.tastybento.bskyblock.managers.island.IslandCache; import us.tastybento.bskyblock.util.Util; @@ -747,8 +760,8 @@ public class IslandsManagerTest { */ @Test public void testLoad() { - IslandsManager im = new IslandsManager(plugin); - im.load(); + //IslandsManager im = new IslandsManager(plugin); + //im.load(); } @@ -839,7 +852,7 @@ public class IslandsManagerTest { */ @Test public void testSave() { - //fail("Not yet implemented"); // TODO + //fail("Not yet implemented"); // TODO - warning saving stuff will go on the file system } /** @@ -882,4 +895,62 @@ public class IslandsManagerTest { //fail("Not yet implemented"); // TODO } + /** + * Test method for {@link us.tastybento.bskyblock.managers.IslandsManager#clearArea(Location)}. + */ + @Test + public void testClearArea() { + WorldSettings ws = mock(WorldSettings.class); + when(iwm.getWorldSettings(Mockito.any())).thenReturn(ws); + Map worldFlags = new HashMap<>(); + when(ws.getWorldFlags()).thenReturn(worldFlags); + + Flags.REMOVE_MOBS.setSetting(world, true); + // Default whitelist + Set 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); + + + Collection collection = new ArrayList<>(); + collection.add(player); + collection.add(zombie); + collection.add(cow); + collection.add(slime); + collection.add(wither); + collection.add(creeper); + when(world + .getNearbyEntities(Mockito.any(Location.class), Mockito.anyDouble(), Mockito.anyDouble(), Mockito.anyDouble())) + .thenReturn(collection); + + IslandsManager im = new IslandsManager(plugin); + im.clearArea(location); + + Mockito.verify(zombie).remove(); + Mockito.verify(player, Mockito.never()).remove(); + Mockito.verify(cow, Mockito.never()).remove(); + Mockito.verify(slime, Mockito.never()).remove(); + Mockito.verify(wither, Mockito.never()).remove(); + Mockito.verify(creeper).remove(); + } + }