Perform block conversions in 3D.

Fixes https://github.com/BentoBoxWorld/Greenhouses/issues/59
This commit is contained in:
tastybento 2020-08-22 17:55:38 -07:00
parent ad82941452
commit 9d85e37e1b
5 changed files with 197 additions and 13 deletions

View File

@ -223,5 +223,4 @@ public class Greenhouse implements DataObject {
public Map<Material, Integer> getMissingBlocks() {
return missingBlocks;
}
}

View File

@ -42,7 +42,7 @@ public class BiomeRecipe implements Comparable<BiomeRecipe> {
private String name;
private String friendlyName;
private final List<BlockFace> adjBlocks = Arrays.asList( BlockFace.DOWN, BlockFace.EAST, BlockFace.NORTH, BlockFace.SOUTH, BlockFace.UP, BlockFace.WEST);
private static final List<BlockFace> ADJ_BLOCKS = Arrays.asList( BlockFace.DOWN, BlockFace.EAST, BlockFace.NORTH, BlockFace.SOUTH, BlockFace.UP, BlockFace.WEST);
// Content requirements
// Material, Type, Qty. There can be more than one type of material required
@ -221,7 +221,7 @@ public class BiomeRecipe implements Comparable<BiomeRecipe> {
.filter(bc -> random.nextDouble() < bc.getProbability())
.forEach(bc -> {
// Check if the block is in the right area, up, down, n,s,e,w
if (adjBlocks.stream().map(b::getRelative).map(Block::getType).anyMatch(m -> bc.getLocalMaterial() == null || m == bc.getLocalMaterial())) {
if (ADJ_BLOCKS.stream().map(b::getRelative).map(Block::getType).anyMatch(m -> bc.getLocalMaterial() == null || m == bc.getLocalMaterial())) {
// Convert!
b.setType(bc.getNewMaterial());
}

View File

@ -37,10 +37,12 @@ public class EcoSystemManager {
public EcoSystemManager(Greenhouses addon, GreenhouseManager greenhouseManager) {
this.addon = addon;
this.g = greenhouseManager;
setup();
}
private void setup() {
/**
* Kick off schedulers
*/
void setup() {
// Kick off flower growing
long plantTick = addon.getSettings().getPlantTick() * 60 * 20L; // In minutes
if (plantTick > 0) {
@ -81,10 +83,16 @@ public class EcoSystemManager {
private void convertBlocks(Greenhouse gh) {
if(!gh.getLocation().getWorld().isChunkLoaded(((int) gh.getBoundingBox().getMaxX()) >> 4, ((int) gh.getBoundingBox().getMaxZ()) >> 4) || !gh.getLocation().getWorld().isChunkLoaded(((int) gh.getBoundingBox().getMinX()) >> 4, ((int) gh.getBoundingBox().getMinZ()) >> 4)){
//addon.log("Skipping convertblock for unloaded greenhouse at " + gh.getLocation());
return;
}
getAvailableBlocks(gh).stream().map(b -> b.getRelative(BlockFace.DOWN)).forEach(gh.getBiomeRecipe()::convertBlock);
for (int x = (int)gh.getBoundingBox().getMinX() + 1; x < (int)gh.getBoundingBox().getMaxX(); x++) {
for (int z = (int)gh.getBoundingBox().getMinZ() + 1; z < (int)gh.getBoundingBox().getMaxZ(); z++) {
for (int y = (int)gh.getBoundingBox().getMaxY() - 2; y >= (int)gh.getBoundingBox().getMinY() && y > 0; y--) {
Block b = gh.getWorld().getBlockAt(x, y, z).getRelative(BlockFace.DOWN);
if (!b.isEmpty()) gh.getBiomeRecipe().convertBlock(b);
}
}
}
}
private void verify(Greenhouse gh) {
@ -121,7 +129,7 @@ public class EcoSystemManager {
.filter(e -> gh.getBiomeRecipe().getMobTypes().contains(e.getType()))
.filter(e -> gh.contains(e.getLocation())).count();
// Get the blocks in the greenhouse where spawning could occur
List<Block> list = new ArrayList<>(getAvailableBlocks(gh));
List<Block> list = new ArrayList<>(getAvailableBlocks(gh, false));
Collections.shuffle(list, new Random(System.currentTimeMillis()));
Iterator<Block> it = list.iterator();
// Check if the greenhouse is full
@ -146,7 +154,7 @@ public class EcoSystemManager {
int bonemeal = getBoneMeal(gh);
if (bonemeal > 0) {
// Get a list of all available blocks
int plantsGrown = getAvailableBlocks(gh).stream().limit(bonemeal).mapToInt(bl -> gh.getBiomeRecipe().growPlant(bl) ? 1 : 0).sum();
int plantsGrown = getAvailableBlocks(gh, false).stream().limit(bonemeal).mapToInt(bl -> gh.getBiomeRecipe().growPlant(bl) ? 1 : 0).sum();
if (plantsGrown > 0) {
setBoneMeal(gh, bonemeal - (int)Math.ceil((double)plantsGrown / PLANTS_PER_BONEMEAL ));
}
@ -170,17 +178,21 @@ public class EcoSystemManager {
}
/**
* Get a list of the lowest level air blocks inside the greenhouse
* Get a list of the lowest level blocks inside the greenhouse. May be air, liquid or plants.
* These blocks sit just above solid blocks
* @param gh - greenhouse
* @param ignoreliquid - true if liquid blocks should be treated like air blocks
* @return List of blocks
*/
private List<Block> getAvailableBlocks(Greenhouse gh) {
List<Block> getAvailableBlocks(Greenhouse gh, boolean ignoreLiquid) {
List<Block> result = new ArrayList<>();
for (int x = (int)gh.getBoundingBox().getMinX() + 1; x < (int)gh.getBoundingBox().getMaxX(); x++) {
for (int z = (int)gh.getBoundingBox().getMinZ() + 1; z < (int)gh.getBoundingBox().getMaxZ(); z++) {
for (int y = (int)gh.getBoundingBox().getMaxY() - 2; y >= (int)gh.getBoundingBox().getMinY(); y--) {
Block b = gh.getLocation().getWorld().getBlockAt(x, y, z);
if ((!b.isEmpty() && !b.isPassable()) && (b.getRelative(BlockFace.UP).isEmpty() || b.getRelative(BlockFace.UP).isPassable())) {
Block b = gh.getWorld().getBlockAt(x, y, z);
if ((!b.isEmpty() && !b.isPassable())
&& (b.getRelative(BlockFace.UP).isEmpty() || b.getRelative(BlockFace.UP).isPassable()
|| (ignoreLiquid && b.getRelative(BlockFace.UP).isLiquid()))) {
result.add(b.getRelative(BlockFace.UP));
break;
}
@ -212,6 +224,9 @@ public class EcoSystemManager {
return (Hopper)gh.getRoofHopperLocation().getBlock().getState();
}
/**
* Cancel all the scheduled tasks
*/
public void cancel() {
plantTask.cancel();
mobTask.cancel();

View File

@ -67,6 +67,7 @@ public class GreenhouseManager implements Listener {
loadGreenhouses();
// Start ecosystems
ecoMgr = new EcoSystemManager(addon, this);
ecoMgr.setup();
// Register listeners
addon.registerListener(new SnowTracker(addon));
addon.registerListener(new GreenhouseEvents(addon));

View File

@ -0,0 +1,169 @@
/**
*
*/
package world.bentobox.greenhouses.managers;
import static org.junit.Assert.*;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyDouble;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import java.util.List;
import org.bukkit.Bukkit;
import org.bukkit.World;
import org.bukkit.block.Block;
import org.bukkit.block.BlockFace;
import org.bukkit.util.BoundingBox;
import org.bukkit.util.Vector;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner;
import world.bentobox.bentobox.BentoBox;
import world.bentobox.greenhouses.data.Greenhouse;
/**
* @author tastybento
*
*/
@RunWith(PowerMockRunner.class)
@PrepareForTest({Bukkit.class, BentoBox.class})
public class EcoSystemManagerTest {
@Mock
private Greenhouse gh;
@Mock
private World world;
@Mock
private Block block;
@Mock
private Block air;
@Mock
private Block liquid;
@Mock
private Block plant;
// CUT
private EcoSystemManager eco;
/**
* @throws java.lang.Exception
*/
@Before
public void setUp() throws Exception {
// 4x4x4 greenhouse
BoundingBox bb = BoundingBox.of(new Vector(0,0,0), new Vector(5,5,5));
when(gh.getBoundingBox()).thenReturn(bb);
// World
when(gh.getWorld()).thenReturn(world);
when(world.getBlockAt(anyInt(), anyInt(), anyInt())).thenReturn(block);
// Block
// Air
when(air.isEmpty()).thenReturn(true);
when(air.isPassable()).thenReturn(true);
when(air.getRelative(eq(BlockFace.UP))).thenReturn(air);
// Plant
when(plant.isPassable()).thenReturn(true);
when(plant.getRelative(eq(BlockFace.UP))).thenReturn(air);
// Liquid
when(liquid.isLiquid()).thenReturn(true);
when(liquid.getRelative(eq(BlockFace.UP))).thenReturn(air);
// Default for block
when(block.getRelative(eq(BlockFace.UP))).thenReturn(air);
eco = new EcoSystemManager(null, null);
}
/**
* @throws java.lang.Exception
*/
@After
public void tearDown() throws Exception {
}
/**
* Test method for {@link world.bentobox.greenhouses.managers.EcoSystemManager#getAvailableBlocks(world.bentobox.greenhouses.data.Greenhouse)}.
*/
@Test
public void testGetAvailableBlocksAirAboveBlock() {
List<Block> result = eco.getAvailableBlocks(gh, false);
assertEquals(16, result.size());
assertEquals(air, result.get(0));
}
/**
* Test method for {@link world.bentobox.greenhouses.managers.EcoSystemManager#getAvailableBlocks(world.bentobox.greenhouses.data.Greenhouse)}.
*/
@Test
public void testGetAvailableBlocksPlantAboveBlock() {
when(block.getRelative(eq(BlockFace.UP))).thenReturn(plant);
List<Block> result = eco.getAvailableBlocks(gh, false);
assertEquals(16, result.size());
assertEquals(plant, result.get(0));
}
/**
* Test method for {@link world.bentobox.greenhouses.managers.EcoSystemManager#getAvailableBlocks(world.bentobox.greenhouses.data.Greenhouse)}.
*/
@Test
public void testGetAvailableBlocksAllAir() {
when(world.getBlockAt(anyInt(), anyInt(), anyInt())).thenReturn(air);
List<Block> result = eco.getAvailableBlocks(gh, false);
assertEquals(0, result.size());
}
/**
* Test method for {@link world.bentobox.greenhouses.managers.EcoSystemManager#getAvailableBlocks(world.bentobox.greenhouses.data.Greenhouse)}.
*/
@Test
public void testGetAvailableBlocksAllLiquid() {
when(liquid.getRelative(eq(BlockFace.UP))).thenReturn(liquid);
when(world.getBlockAt(anyInt(), anyInt(), anyInt())).thenReturn(liquid);
List<Block> result = eco.getAvailableBlocks(gh, false);
assertEquals(0, result.size());
}
/**
* Test method for {@link world.bentobox.greenhouses.managers.EcoSystemManager#getAvailableBlocks(world.bentobox.greenhouses.data.Greenhouse)}.
*/
@Test
public void testGetAvailableBlocksAllPlant() {
when(plant.getRelative(eq(BlockFace.UP))).thenReturn(plant);
when(world.getBlockAt(anyInt(), anyInt(), anyInt())).thenReturn(plant);
List<Block> result = eco.getAvailableBlocks(gh, false);
assertEquals(0, result.size());
}
/**
* Test method for {@link world.bentobox.greenhouses.managers.EcoSystemManager#getAvailableBlocks(world.bentobox.greenhouses.data.Greenhouse)}.
*/
@Test
public void testGetAvailableBlocksLiquidAboveBlockIgnoreLiquids() {
when(block.getRelative(eq(BlockFace.UP))).thenReturn(liquid);
List<Block> result = eco.getAvailableBlocks(gh, true);
assertEquals(16, result.size());
assertEquals(liquid, result.get(0));
}
/**
* Test method for {@link world.bentobox.greenhouses.managers.EcoSystemManager#getAvailableBlocks(world.bentobox.greenhouses.data.Greenhouse)}.
*/
@Test
public void testGetAvailableBlocksLiquidAboveBlock() {
when(block.getRelative(eq(BlockFace.UP))).thenReturn(liquid);
List<Block> result = eco.getAvailableBlocks(gh, false);
assertEquals(0, result.size());
}
}