Added Flags#LIQUIDS_FLOWING_OUT

Also fixed some Javadoc in Flags, and I've also done my very first test unit!

#511
This commit is contained in:
Florian CUNY 2019-02-03 14:05:28 +01:00
parent 30fb7820fc
commit 1c06584131
4 changed files with 247 additions and 2 deletions

View File

@ -0,0 +1,44 @@
package world.bentobox.bentobox.listeners.flags.worldsettings;
import org.bukkit.block.Block;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.block.BlockFromToEvent;
import world.bentobox.bentobox.api.flags.FlagListener;
import world.bentobox.bentobox.lists.Flags;
/**
* Handles {@link world.bentobox.bentobox.lists.Flags#LIQUIDS_FLOWING_OUT}.
* @author Poslovitch
* @since 1.3.0
*/
public class LiquidsFlowingOutListener extends FlagListener {
@EventHandler(priority = EventPriority.NORMAL)
public void onLiquidFlow(BlockFromToEvent e) {
Block from = e.getBlock();
if (!from.isLiquid()) {
return;
}
Block to = e.getToBlock();
if (!getIWM().inWorld(from.getLocation()) || Flags.LIQUIDS_FLOWING_OUT.isSetForWorld(from.getWorld())) {
// We do not want to run any check if this is not the right world or if it is allowed.
return;
}
// TODO: Make a less restrictive check - try to see where the border is, so that
// water can still flows sideways.
// see: https://github.com/BentoBoxWorld/BentoBox/issues/511#issuecomment-460040287
// Time to do some maths! We've got the vector FromTo, let's check if its y coordinate is different from zero.
if (to.getLocation().toVector().subtract(from.getLocation().toVector()).getY() != 0) {
// We do not run any checks if this is a vertical flow - would be too much resource consuming.
return;
}
// Only prevent if it is flowing into the area between islands.
if (!getIslands().getProtectedIslandAt(to.getLocation()).isPresent()) {
e.setCancelled(true);
}
}
}

View File

@ -10,6 +10,7 @@ import org.bukkit.Material;
import world.bentobox.bentobox.api.flags.Flag; import world.bentobox.bentobox.api.flags.Flag;
import world.bentobox.bentobox.api.flags.Flag.Type; import world.bentobox.bentobox.api.flags.Flag.Type;
import world.bentobox.bentobox.api.flags.clicklisteners.CycleClick; import world.bentobox.bentobox.api.flags.clicklisteners.CycleClick;
import world.bentobox.bentobox.listeners.flags.worldsettings.LiquidsFlowingOutListener;
import world.bentobox.bentobox.listeners.flags.worldsettings.ObsidianScoopingListener; import world.bentobox.bentobox.listeners.flags.worldsettings.ObsidianScoopingListener;
import world.bentobox.bentobox.listeners.flags.protection.BlockInteractionListener; import world.bentobox.bentobox.listeners.flags.protection.BlockInteractionListener;
import world.bentobox.bentobox.listeners.flags.protection.BreakBlocksListener; import world.bentobox.bentobox.listeners.flags.protection.BreakBlocksListener;
@ -73,7 +74,7 @@ public final class Flags {
public static final Flag PLACE_BLOCKS = new Flag.Builder("PLACE_BLOCKS", Material.GRASS).listener(new PlaceBlocksListener()).build(); public static final Flag PLACE_BLOCKS = new Flag.Builder("PLACE_BLOCKS", Material.GRASS).listener(new PlaceBlocksListener()).build();
/** /**
* Prevents players from generated Frosted Ice on one's island using boots enchanted with "Frost Walker". * Prevents players from generating Frosted Ice on one's island using "Frost Walker" enchanted boots.
* @see PlaceBlocksListener * @see PlaceBlocksListener
*/ */
public static final Flag FROST_WALKER = new Flag.Builder("FROST_WALKER", Material.ICE).build(); public static final Flag FROST_WALKER = new Flag.Builder("FROST_WALKER", Material.ICE).build();
@ -129,7 +130,7 @@ public final class Flags {
// Throwing things // Throwing things
public static final Flag EGGS = new Flag.Builder("EGGS", Material.EGG).listener(new EggListener()).build(); public static final Flag EGGS = new Flag.Builder("EGGS", Material.EGG).listener(new EggListener()).build();
/** /**
* Prevents players from throwing potions / exp bottles. * Prevents players from throwing potions / experience bottles.
* @since 1.1 * @since 1.1
*/ */
public static final Flag POTION_THROWING = new Flag.Builder("POTION_THROWING", Material.SPLASH_POTION).listener(new ThrowingListener()).build(); public static final Flag POTION_THROWING = new Flag.Builder("POTION_THROWING", Material.SPLASH_POTION).listener(new ThrowingListener()).build();
@ -271,6 +272,14 @@ public final class Flags {
public static final Flag OBSIDIAN_SCOOPING = new Flag.Builder("OBSIDIAN_SCOOPING", Material.OBSIDIAN).type(Type.WORLD_SETTING) public static final Flag OBSIDIAN_SCOOPING = new Flag.Builder("OBSIDIAN_SCOOPING", Material.OBSIDIAN).type(Type.WORLD_SETTING)
.listener(new ObsidianScoopingListener()).defaultSetting(true).build(); .listener(new ObsidianScoopingListener()).defaultSetting(true).build();
/**
* Prevents liquids from flowing outside the protection range, in order to avoid cobblestone/stone/obsidian being generated and remaining unbreakable by players.
* @since 1.3.0
* @see LiquidsFlowingOutListener
*/
public static final Flag LIQUIDS_FLOWING_OUT = new Flag.Builder("LIQUIDS_FLOWING_OUT", Material.WATER_BUCKET).type(Type.WORLD_SETTING)
.listener(new LiquidsFlowingOutListener()).build();
/** /**
* Provides a list of all the Flag instances contained in this class using reflection. * Provides a list of all the Flag instances contained in this class using reflection.
* @return List of all the flags in this class * @return List of all the flags in this class

View File

@ -651,6 +651,14 @@ protection:
description: "Toggle use" description: "Toggle use"
name: "Lever use" name: "Lever use"
hint: "No lever use" hint: "No lever use"
LIQUIDS_FLOWING_OUT:
name: "Liquids flowing outside islands"
description: |-
&aToggle whether liquids can flow outside
&aof the island's protection range.
&aDisabling it helps avoiding lava and water
&agenerating cobblestone in the area between
&atwo islands.
LOCK: LOCK:
description: "Toggle lock" description: "Toggle lock"
name: "Lock island" name: "Lock island"

View File

@ -0,0 +1,184 @@
package world.bentobox.bentobox.listeners.flags;
import org.bukkit.Location;
import org.bukkit.World;
import org.bukkit.block.Block;
import org.bukkit.event.block.BlockFromToEvent;
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 org.powermock.reflect.Whitebox;
import world.bentobox.bentobox.BentoBox;
import world.bentobox.bentobox.api.configuration.WorldSettings;
import world.bentobox.bentobox.database.objects.Island;
import world.bentobox.bentobox.listeners.flags.worldsettings.LiquidsFlowingOutListener;
import world.bentobox.bentobox.lists.Flags;
import world.bentobox.bentobox.managers.IslandWorldManager;
import world.bentobox.bentobox.managers.IslandsManager;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.mockito.Matchers.any;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
/**
* Tests {@link world.bentobox.bentobox.listeners.flags.worldsettings.LiquidsFlowingOutListener}.
* @author Poslovitch
* @since 1.3.0
*/
@RunWith(PowerMockRunner.class)
@PrepareForTest({BentoBox.class})
public class LiquidsFlowingOutListenerTest {
/* IslandWorldManager */
private IslandWorldManager iwm;
/* Blocks */
private Block from;
private Block to;
/* Event */
private BlockFromToEvent event;
/* World */
private World world;
/* Islands */
private IslandsManager islandsManager;
@Before
public void setUp() throws Exception {
// Set up plugin
BentoBox plugin = mock(BentoBox.class);
Whitebox.setInternalState(BentoBox.class, "instance", plugin);
/* Blocks */
from = mock(Block.class);
when(from.isLiquid()).thenReturn(true);
to = mock(Block.class);
/* World */
world = mock(World.class);
when(from.getWorld()).thenReturn(world);
// Give them locations
Location fromLocation = new Location(world, 0, 0, 0);
when(from.getLocation()).thenReturn(fromLocation);
Location toLocation = new Location(world, 1, 0, 0);
when(to.getLocation()).thenReturn(toLocation);
/* Event */
event = new BlockFromToEvent(from, to);
/* Island World Manager */
iwm = mock(IslandWorldManager.class);
when(plugin.getIWM()).thenReturn(iwm);
// WorldSettings and World Flags
WorldSettings ws = mock(WorldSettings.class);
when(iwm.getWorldSettings(Mockito.any())).thenReturn(ws);
Map<String, Boolean> worldFlags = new HashMap<>();
when(ws.getWorldFlags()).thenReturn(worldFlags);
// By default everything is in world
when(iwm.inWorld(any(World.class))).thenReturn(true);
when(iwm.inWorld(any(Location.class))).thenReturn(true);
/* Flags */
// By default, it is not allowed
Flags.LIQUIDS_FLOWING_OUT.setSetting(world, false);
/* Islands */
islandsManager = mock(IslandsManager.class);
when(plugin.getIslands()).thenReturn(islandsManager);
// By default, there should be no island's protection range at toLocation.
when(islandsManager.getProtectedIslandAt(toLocation)).thenReturn(Optional.empty());
}
/**
* Asserts that the event is never cancelled when the 'from' block is not liquid.
*/
@Test
public void testFromIsNotLiquid() {
// The 'from' block is not liquid
when(from.isLiquid()).thenReturn(false);
// Run
new LiquidsFlowingOutListener().onLiquidFlow(event);
assertFalse(event.isCancelled());
}
/**
* Asserts that the event is never cancelled when the 'from' block is not in the world.
*/
@Test
public void testFromIsNotInWorld() {
// Not in world
when(iwm.inWorld(any(World.class))).thenReturn(false);
when(iwm.inWorld(any(Location.class))).thenReturn(false);
// Run
new LiquidsFlowingOutListener().onLiquidFlow(event);
assertFalse(event.isCancelled());
}
/**
* Asserts that the event is never cancelled when {@link Flags#LIQUIDS_FLOWING_OUT} is allowed.
*/
@Test
public void testFlagIsAllowed() {
// Allowed
Flags.LIQUIDS_FLOWING_OUT.setSetting(world, true);
// Run
new LiquidsFlowingOutListener().onLiquidFlow(event);
assertFalse(event.isCancelled());
}
/**
* Asserts that the event is never cancelled when the liquid flows vertically.
*/
@Test
public void testLiquidFlowsVertically() {
// "To" is at (1,0,0)
// Set "from" at (1,1,0) so that the vector's y coordinate != 0, which means the liquid flows vertically.
when(from.getLocation()).thenReturn(new Location(world, 1, 1, 0));
// Run
new LiquidsFlowingOutListener().onLiquidFlow(event);
assertFalse(event.isCancelled());
}
/**
* Asserts that the event is never cancelled when the liquid flows to a location in an island's protection range.
*/
@Test
public void testLiquidFlowsToLocationInIslandProtectionRange() {
// There's a protected island at the "to"
Island island = mock(Island.class);
when(islandsManager.getProtectedIslandAt(to.getLocation())).thenReturn(Optional.of(island));
// Run
new LiquidsFlowingOutListener().onLiquidFlow(event);
assertFalse(event.isCancelled());
}
/**
* Asserts that the event is cancelled with the default configuration provided in {@link LiquidsFlowingOutListenerTest#setUp()}.
*/
@Test
public void testLiquidFlowIsBlocked() {
// Run
new LiquidsFlowingOutListener().onLiquidFlow(event);
assertTrue(event.isCancelled());
}
}