From aed648e6e766cb272475d1960cbbe041d4299cb3 Mon Sep 17 00:00:00 2001 From: tastybento Date: Mon, 4 Jun 2018 19:24:11 -0700 Subject: [PATCH] Added Piston Push setting. Allows/Disallows piston pushing outside an island. This is a worldwide setting for admins. https://github.com/tastybento/bskyblock/projects/3#card-10260101 --- locales/en-US.yml | 5 + .../listeners/flags/PistonPushListener.java | 32 +++++ .../us/tastybento/bskyblock/lists/Flags.java | 13 +- .../flags/EnterExitListenerTest.java | 2 +- .../flags/PistonPushListenerTest.java | 130 ++++++++++++++++++ 5 files changed, 178 insertions(+), 4 deletions(-) create mode 100644 src/main/java/us/tastybento/bskyblock/listeners/flags/PistonPushListener.java create mode 100644 src/test/java/us/tastybento/bskyblock/listeners/flags/PistonPushListenerTest.java diff --git a/locales/en-US.yml b/locales/en-US.yml index 24f2ddbd2..67a3e0190 100644 --- a/locales/en-US.yml +++ b/locales/en-US.yml @@ -343,6 +343,11 @@ protection: RIDING: "RIDING" MUSIC: "MUSIC" CHEST: "CHEST" + PISTON_PUSH: + name: "Piston Push" + description: | + &aAllow pistons to push + &ablocks outside island PLACE_BLOCKS: "PLACE_BLOCKS" PORTAL: "PORTAL" PRESSURE_PLATE: "PRESSURE_PLATE" diff --git a/src/main/java/us/tastybento/bskyblock/listeners/flags/PistonPushListener.java b/src/main/java/us/tastybento/bskyblock/listeners/flags/PistonPushListener.java new file mode 100644 index 000000000..ee5c1468a --- /dev/null +++ b/src/main/java/us/tastybento/bskyblock/listeners/flags/PistonPushListener.java @@ -0,0 +1,32 @@ +/** + * + */ +package us.tastybento.bskyblock.listeners.flags; + +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.block.BlockPistonExtendEvent; + +import us.tastybento.bskyblock.lists.Flags; + +/** + * Prevents pistons from pushing blocks outside island protection range + * @author tastybento + * + */ +public class PistonPushListener extends AbstractFlagListener { + + @EventHandler(priority = EventPriority.LOW, ignoreCancelled = true) + public void onPistonExtend(BlockPistonExtendEvent e) { + // Only process if flag is active + if (Flags.PISTON_PUSH.isSet(e.getBlock().getWorld())) { + getIslands().getProtectedIslandAt(e.getBlock().getLocation()).ifPresent(i -> + e.setCancelled( + // Run through the location of all the relative blocks and see if they are outside the island + !e.getBlocks().stream() + .map(b -> b.getRelative(e.getDirection()).getLocation()) + // All blocks must be on the island, otherwise the event is cancelled + .allMatch(i::onIsland))); + } + } +} diff --git a/src/main/java/us/tastybento/bskyblock/lists/Flags.java b/src/main/java/us/tastybento/bskyblock/lists/Flags.java index ba1415daf..7a47c4dff 100644 --- a/src/main/java/us/tastybento/bskyblock/lists/Flags.java +++ b/src/main/java/us/tastybento/bskyblock/lists/Flags.java @@ -25,6 +25,7 @@ import us.tastybento.bskyblock.listeners.flags.LeashListener; import us.tastybento.bskyblock.listeners.flags.LockAndBanListener; import us.tastybento.bskyblock.listeners.flags.PVPListener; import us.tastybento.bskyblock.listeners.flags.PhysicalInteractionListener; +import us.tastybento.bskyblock.listeners.flags.PistonPushListener; import us.tastybento.bskyblock.listeners.flags.PlaceBlocksListener; import us.tastybento.bskyblock.listeners.flags.PortalListener; import us.tastybento.bskyblock.listeners.flags.SettingsToggleClickListener; @@ -134,13 +135,19 @@ public class Flags { public static final Flag PVP_NETHER = new FlagBuilder().id("PVP_NETHER").icon(Material.IRON_AXE).type(Type.SETTING).build(); public static final Flag PVP_END = new FlagBuilder().id("PVP_END").icon(Material.END_CRYSTAL).type(Type.SETTING).build(); // Others + public static final Flag ANIMAL_SPAWN = new FlagBuilder().id("ANIMAL_SPAWN").icon(Material.APPLE).allowedByDefault(true).type(Type.SETTING).build(); + public static final Flag MONSTER_SPAWN = new FlagBuilder().id("MONSTER_SPAWN").icon(Material.MOB_SPAWNER).allowedByDefault(true).type(Type.SETTING).build(); + public static final Flag FIRE_SPREAD = new FlagBuilder().id("FIRE_SPREAD").icon(Material.FIREWORK_CHARGE).type(Type.SETTING).build(); + // Global flags (apply to every island) public static final Flag ENTER_EXIT_MESSAGES = new FlagBuilder().id("ENTER_EXIT_MESSAGES").icon(Material.DIRT).allowedByDefault(true).type(Type.SETTING) .listener(new EnterExitListener()) .onClick(new SettingsToggleClickListener("ENTER_EXIT_MESSAGES")) .build(); - public static final Flag ANIMAL_SPAWN = new FlagBuilder().id("ANIMAL_SPAWN").icon(Material.APPLE).allowedByDefault(true).type(Type.SETTING).build(); - public static final Flag MONSTER_SPAWN = new FlagBuilder().id("MONSTER_SPAWN").icon(Material.MOB_SPAWNER).allowedByDefault(true).type(Type.SETTING).build(); - public static final Flag FIRE_SPREAD = new FlagBuilder().id("FIRE_SPREAD").icon(Material.FIREWORK_CHARGE).type(Type.SETTING).build(); + public static final Flag PISTON_PUSH = new FlagBuilder().id("PISTON_PUSH").icon(Material.PISTON_BASE).allowedByDefault(true).type(Type.SETTING) + .listener(new PistonPushListener()) + .onClick(new SettingsToggleClickListener("PISTON_PUSH")) + .build(); + /** * @return List of all the flags in this class diff --git a/src/test/java/us/tastybento/bskyblock/listeners/flags/EnterExitListenerTest.java b/src/test/java/us/tastybento/bskyblock/listeners/flags/EnterExitListenerTest.java index 3fa0205e3..5e64aa571 100644 --- a/src/test/java/us/tastybento/bskyblock/listeners/flags/EnterExitListenerTest.java +++ b/src/test/java/us/tastybento/bskyblock/listeners/flags/EnterExitListenerTest.java @@ -100,7 +100,7 @@ public class EnterExitListenerTest { notifier = mock(Notifier.class); when(plugin.getNotifier()).thenReturn(notifier); - // Island Banned list initialization + // Island initialization island = mock(Island.class); Location loc = mock(Location.class); when(loc.getWorld()).thenReturn(world); diff --git a/src/test/java/us/tastybento/bskyblock/listeners/flags/PistonPushListenerTest.java b/src/test/java/us/tastybento/bskyblock/listeners/flags/PistonPushListenerTest.java new file mode 100644 index 000000000..2e9d6fdca --- /dev/null +++ b/src/test/java/us/tastybento/bskyblock/listeners/flags/PistonPushListenerTest.java @@ -0,0 +1,130 @@ +package us.tastybento.bskyblock.listeners.flags; + +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 java.util.Optional; +import java.util.UUID; + +import org.bukkit.Location; +import org.bukkit.World; +import org.bukkit.block.Block; +import org.bukkit.block.BlockFace; +import org.bukkit.event.block.BlockPistonExtendEvent; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mockito; +import org.powermock.api.mockito.PowerMockito; +import org.powermock.core.classloader.annotations.PrepareForTest; +import org.powermock.modules.junit4.PowerMockRunner; +import org.powermock.reflect.Whitebox; + +import us.tastybento.bskyblock.BSkyBlock; +import us.tastybento.bskyblock.database.objects.Island; +import us.tastybento.bskyblock.lists.Flags; +import us.tastybento.bskyblock.managers.IslandsManager; +import us.tastybento.bskyblock.util.Util; + +@RunWith(PowerMockRunner.class) +@PrepareForTest({BSkyBlock.class, Util.class }) +public class PistonPushListenerTest { + + private Island island; + private IslandsManager im; + private World world; + private Location inside; + private UUID uuid; + private Block block; + private List blocks; + private Block blockPushed; + + @Before + public void setUp() throws Exception { + // 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); + + im = mock(IslandsManager.class); + when(plugin.getIslands()).thenReturn(im); + when(im.getIsland(Mockito.any(), Mockito.any(UUID.class))).thenReturn(island); + + inside = mock(Location.class); + + Optional opIsland = Optional.ofNullable(island); + when(im.getProtectedIslandAt(Mockito.eq(inside))).thenReturn(opIsland); + + // Blocks + block = mock(Block.class); + when(block.getWorld()).thenReturn(world); + when(block.getLocation()).thenReturn(inside); + + blockPushed = mock(Block.class); + + when(block.getRelative(Mockito.any(BlockFace.class))).thenReturn(blockPushed); + + // The blocks in the pushed list are all inside the island + when(blockPushed.getLocation()).thenReturn(inside); + + // Make a list of ten blocks + blocks = new ArrayList<>(); + for (int i = 0; i < 10; i++) { + blocks.add(block); + } + + PowerMockito.mockStatic(Util.class); + when(Util.getWorld(Mockito.any())).thenReturn(world); + + Flags.PISTON_PUSH.setSetting(world, true); + } + + @Test + public void testOnPistonExtendFlagNotSet() { + Flags.PISTON_PUSH.setSetting(world, false); + BlockPistonExtendEvent e = new BlockPistonExtendEvent(block, blocks, BlockFace.EAST); + new PistonPushListener().onPistonExtend(e); + + // Should fail because flag is not set + assertFalse(e.isCancelled()); + } + + @Test + public void testOnPistonExtendFlagSetOnIsland() { + + // The blocks in the pushed list are all inside the island + when(island.onIsland(Mockito.any())).thenReturn(true); + + BlockPistonExtendEvent e = new BlockPistonExtendEvent(block, blocks, BlockFace.EAST); + new PistonPushListener().onPistonExtend(e); + + // Should fail because on island + assertFalse(e.isCancelled()); + } + + @Test + public void testOnPistonExtendFlagSetOffIsland() { + // The blocks in the pushed list are all outside the island + when(island.onIsland(Mockito.any())).thenReturn(false); + + BlockPistonExtendEvent e = new BlockPistonExtendEvent(block, blocks, BlockFace.EAST); + new PistonPushListener().onPistonExtend(e); + + // Should fail because on island + assertTrue(e.isCancelled()); + } + +}