Added Flags#TREES_GROWING_OUTSIDE_RANGE

#512
This commit is contained in:
Florian CUNY 2019-02-09 11:39:12 +01:00
parent 69ca910df4
commit b8afd1919f
4 changed files with 254 additions and 0 deletions

View File

@ -0,0 +1,33 @@
package world.bentobox.bentobox.listeners.flags.worldsettings;
import org.bukkit.Material;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.world.StructureGrowEvent;
import world.bentobox.bentobox.api.flags.FlagListener;
import world.bentobox.bentobox.lists.Flags;
/**
* Handles {@link world.bentobox.bentobox.lists.Flags#TREES_GROWING_OUTSIDE_RANGE}.
* @author Poslovitch
* @since 1.3.0
*/
public class TreesGrowingOutsideRangeListener extends FlagListener {
@EventHandler(priority = EventPriority.LOWEST)
public void onTreeGrow(StructureGrowEvent e) {
if (!getIWM().inWorld(e.getWorld()) || Flags.TREES_GROWING_OUTSIDE_RANGE.isSetForWorld(e.getWorld())) {
// We do not want to run any check if this is not the right world or if it is allowed.
return;
}
// If there is no protected island at the location of the sapling, just cancel the event (prevents the sapling from growing).
if (!getIslands().getProtectedIslandAt(e.getLocation()).isPresent()) {
e.setCancelled(true);
return;
}
// Now, run through all the blocks that will be generated and if there is no protected island at their location, turn them into AIR.
e.getBlocks().stream().filter(blockState -> !getIslands().getProtectedIslandAt(blockState.getLocation()).isPresent()).forEach(blockState -> blockState.setType(Material.AIR));
}
}

View File

@ -50,6 +50,7 @@ import world.bentobox.bentobox.listeners.flags.worldsettings.ObsidianScoopingLis
import world.bentobox.bentobox.listeners.flags.worldsettings.OfflineRedstoneListener;
import world.bentobox.bentobox.listeners.flags.worldsettings.PistonPushListener;
import world.bentobox.bentobox.listeners.flags.worldsettings.RemoveMobsListener;
import world.bentobox.bentobox.listeners.flags.worldsettings.TreesGrowingOutsideRangeListener;
import world.bentobox.bentobox.managers.RanksManager;
/**
@ -291,9 +292,20 @@ public final class Flags {
/**
* Enables toggling for removal of the end exit island. May not be required on some servers, e.g. PaperSpigot.
* @since 1.3.0
* @see world.bentobox.bentobox.listeners.BlockEndDragon
*/
public static final Flag REMOVE_END_EXIT_ISLAND = new Flag.Builder("REMOVE_END_EXIT_ISLAND", Material.DRAGON_HEAD).type(Type.WORLD_SETTING).defaultSetting(true).build();
/**
* Toggles whether trees can grow outside an island's protection range or not.
* Not only will it prevent saplings placed outside an island's protection range from growing, but it will also block generation of leaves/logs outside of it, thus "cutting" the tree.
* It is disabled by default in order to avoid leaves/logs being generated outside an island's protection range and remaining unbreakable by players.
*
* @since 1.3.0
* @see TreesGrowingOutsideRangeListener
*/
public static final Flag TREES_GROWING_OUTSIDE_RANGE = new Flag.Builder("TREES_GROWING_OUTSIDE_RANGE", Material.OAK_SAPLING).type(Type.WORLD_SETTING).listener(new TreesGrowingOutsideRangeListener()).build();
/**
* Provides a list of all the Flag instances contained in this class using reflection.
* @return List of all the flags in this class

View File

@ -790,6 +790,16 @@ protection:
description: "Toggle access"
name: "Trap doors"
hint: "No trapdoor use"
TREES_GROWING_OUTSIDE_RANGE:
name: "Trees growing outside range"
description: |-
&aToggle whether trees can grow outside an
&aisland's protection range or not.
&aNot only will it prevent saplings placed
&aoutside an island's protection range from
&agrowing, but it will also block generation
&aof leaves/logs outside of the island, thus
&acutting the tree.
TURTLE_EGGS:
description: "Toggle crushing"
name: "Turtle Eggs"

View File

@ -0,0 +1,199 @@
package world.bentobox.bentobox.listeners.flags.worldsettings;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.TreeType;
import org.bukkit.World;
import org.bukkit.block.Block;
import org.bukkit.block.BlockState;
import org.bukkit.event.world.StructureGrowEvent;
import org.junit.Before;
import org.junit.Ignore;
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.lists.Flags;
import world.bentobox.bentobox.managers.IslandWorldManager;
import world.bentobox.bentobox.managers.IslandsManager;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import static org.junit.Assert.assertEquals;
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 TreesGrowingOutsideRangeListener}.
* @author Poslovitch
* @since 1.3.0
*/
@RunWith(PowerMockRunner.class)
@PrepareForTest({BentoBox.class})
public class TreesGrowingOutsideRangeListenerTest {
/* IslandWorldManager */
private IslandWorldManager iwm;
/* Event */
private StructureGrowEvent event;
/* Block */
private Block sapling;
private List<BlockState> blockStates;
/* 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 */
sapling = mock(Block.class);
when(sapling.getType()).thenReturn(Material.OAK_SAPLING);
when(sapling.getLocation()).thenReturn(new Location(world, 2, 0, 2));
blockStates = new ArrayList<>();
populateBlockStatesList();
/* Event */
event = new StructureGrowEvent(sapling.getLocation(), TreeType.TREE, false, null, blockStates);
/* World */
world = mock(World.class);
/* 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.TREES_GROWING_OUTSIDE_RANGE.setSetting(world, false);
/* Islands */
islandsManager = mock(IslandsManager.class);
when(plugin.getIslands()).thenReturn(islandsManager);
// By default, there should be an island.
Island island = mock(Island.class);
when(islandsManager.getProtectedIslandAt(Mockito.any())).thenReturn(Optional.of(island));
}
/**
* Populates {@link TreesGrowingOutsideRangeListenerTest#blockStates} with a tree schema.
*/
private void populateBlockStatesList() {
// Tree logs
for (int i = 0; i < 3; i++) {
BlockState logState = mock(BlockState.class);
when(logState.getType()).thenReturn(Material.OAK_LOG);
when(logState.getLocation()).thenReturn(new Location(world, 2, i, 2));
blockStates.add(logState);
}
// Basic leaves pattern
for (int x = 0; x < 5 ; x++) {
for (int y = 0; y < 5; y++) {
for (int z = 0; z < 5; z++) {
if (x != 2 && y >= 3 && z != 2) {
BlockState leafState = mock(BlockState.class);
when(leafState.getType()).thenReturn(Material.OAK_LEAVES);
when(leafState.getLocation()).thenReturn(new Location(world, x, y, z));
blockStates.add(leafState);
}
}
}
}
}
/**
* Asserts that no interaction is done to the event when it does not happen in the world.
*/
@Test
public void testNotInWorld() {
// Not in world
when(iwm.inWorld(any(World.class))).thenReturn(false);
when(iwm.inWorld(any(Location.class))).thenReturn(false);
// Run
new TreesGrowingOutsideRangeListener().onTreeGrow(event);
assertEquals(blockStates, event.getBlocks());
assertFalse(event.isCancelled());
}
/**
* Asserts that no interaction is done to the event when {@link Flags#TREES_GROWING_OUTSIDE_RANGE} is allowed.
*/
@Test
public void testFlagIsAllowed() {
// Allowed
Flags.TREES_GROWING_OUTSIDE_RANGE.setSetting(world, true);
// Run
new TreesGrowingOutsideRangeListener().onTreeGrow(event);
assertEquals(blockStates, event.getBlocks());
assertFalse(event.isCancelled());
}
/**
* Asserts that the event is cancelled and that there is no interaction with the blocks list when the sapling is outside an island.
*/
@Test
public void testSaplingOutsideIsland() {
// No protected island at the sapling's location
when(islandsManager.getProtectedIslandAt(sapling.getLocation())).thenReturn(Optional.empty());
// Run
new TreesGrowingOutsideRangeListener().onTreeGrow(event);
assertEquals(blockStates, event.getBlocks());
assertTrue(event.isCancelled());
}
/**
* Asserts that no interaction is done to the event when everything's inside an island.
*/
@Test
public void testTreeFullyInsideIsland() {
// Run
new TreesGrowingOutsideRangeListener().onTreeGrow(event);
assertEquals(blockStates, event.getBlocks());
assertFalse(event.isCancelled());
}
@Ignore
@Test
public void testTreePartiallyOutsideIsland() {
//FIXME
// Run
new TreesGrowingOutsideRangeListener().onTreeGrow(event);
assertFalse(event.isCancelled());
}
}